欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

ChatGpt都使用的Java BPE分詞算法不要了解一下

 更新時間:2023年06月01日 09:52:18   作者:蜀山劍客李沐白  
Byte Pair Encoding(BPE)是一種文本壓縮算法,它通常用于自然語言處理領(lǐng)域中的分詞、詞匯表構(gòu)建等任務,本文將對 BPE 算法進行全面、詳細的講解,并提供 Java 相關(guān)的代碼示例,希望對大家有所幫助

Byte Pair Encoding(BPE)是一種文本壓縮算法,它通常用于自然語言處理領(lǐng)域中的分詞、詞匯表構(gòu)建等任務。BPE 算法的核心思想是通過不斷地合并字符或子詞來生成詞匯表。

在這里,我們將對 BPE 算法進行全面、詳細的講解,并提供 Java 相關(guān)的代碼示例。整篇文章大約 8000 字。

1. BPE 算法的原理

BPE 算法的主要思想是將輸入的文本進行多輪迭代的分段和統(tǒng)計,每次迭代都會找到出現(xiàn)頻率最高的相鄰字符或子詞序列,并將其合并成一個新的符號(或單詞)。在整個過程中,所有出現(xiàn)過的字符和新合并出的子詞都被保存在一個詞匯表中。

下面,我們將從以下幾個方面對 BPE 算法的原理進行詳細闡述:

  • 如何定義頻率?
  • 如何生成初始的詞匯表?
  • 如何進行迭代合并?
  • 如何使用 BPE 對文本進行編碼和解碼?

1.1. 如何定義頻率

在 BPE 算法中,頻率的定義非常重要。具體來說,頻率需要考慮字符(單字母)和子詞(多個字母組成的詞)兩個方面。

對于字符而言,我們可以使用它在輸入文本中出現(xiàn)的次數(shù)作為其頻率。例如,如果字符“a”在輸入文本中出現(xiàn)了 10 次,那么我們就認為該字符的頻率為 10。

對于子詞而言,頻率的定義則需要考慮其實際出現(xiàn)的次數(shù)和合并次數(shù)兩個因素。具體來說,如果一個子詞出現(xiàn)了一次,則它的頻率為 1;如果一個子詞被合并了 k 次,則它的頻率需要乘以 2^k。這是因為 BPE 算法中每次合并時都會將原來出現(xiàn)的兩個子詞用新的合并后的子詞替換,這樣會導致原來的子詞從輸入中消失,而新的子詞的頻率則要加上原來的兩個子詞的頻率之和。

1.2. 如何生成初始的詞匯表

BPE 算法的初始詞匯表通常由輸入文本中的所有字符組成。如果某個字符在輸入文本中沒有出現(xiàn)過,那么它不應該加入初始詞匯表。在實際應用中,我們通常會額外添加一些特殊的字符,例如空格、句點、問號等,以便在后續(xù)操作中更方便地進行處理。

1.3. 如何進行迭代合并

在 BPE 算法的每次迭代中,我們會選擇出現(xiàn)頻率最高的相鄰字符或子詞,將它們合并成一個新的符號(或單詞),并將這個新的符號加入到詞匯表中。這個過程一直持續(xù)到達到指定的詞匯表大小為止。

具體來說,BPE 算法的迭代過程通常包括以下幾步:

  • 計算每對相鄰字符或子詞的頻率;
  • 找到出現(xiàn)頻率最高的相鄰字符或子詞,并將它們合并成一個新的符號;
  • 在詞匯表中添加這個新的符號;
  • 更新輸入文本中的所有相鄰字符或子詞,用新的符號替換它們;
  • 重新計算各對相鄰字符或子詞的頻率,回到步驟 2。

在 BPE 算法中,相鄰字符或子詞的選擇是基于前綴和后綴的組合。例如,“app”和“le”可以組成“apple”,“p”和“i”可以組成“pi”,等等。在每一輪迭代中,我們按照從左到右、從上到下的順序遍歷輸入文本,找到出現(xiàn)頻率最高的相鄰字符或子詞,然后進行合并。由于更新后的文本中出現(xiàn)的新的相鄰字符或子詞可能也會成為下一輪迭代中的候選,因此我們需要反復迭代,直到達到指定的詞匯表大小為止。

1.4. 如何使用 BPE 對文本進行編碼和解碼

BPE 算法的最終目的是生成一個包含所有輸入文本中出現(xiàn)的字符和子詞的詞匯表。在使用 BPE 對文本進行編碼和解碼時,我們通常會根據(jù)生成的詞匯表將輸入文本分割成最小的可處理單位,稱為 subword(子詞)。

在對文本進行編碼時,我們可以將每個子詞編碼成它在詞匯表中的索引。如果某個子詞不在詞匯表中,我們可以將它拆分成更小的子詞,并將它們分別編碼。編碼后的結(jié)果通常是一個由整數(shù)構(gòu)成的序列。

在對文本進行解碼時,我們可以根據(jù)詞匯表中的索引將每個子詞解碼成對應的字符串,并將它們拼接起來得到原始文本。如果某個子詞無法解碼,我們可以嘗試將它拆分成更小的子詞,并將它們分別解碼。

2. BPE 算法的實現(xiàn)

在這一篇文章中,我們將使用 Java 語言實現(xiàn) BPE 算法,并演示如何對輸入文本進行編碼和解碼。具體來說,我們將實現(xiàn)以下幾個功能:

  • 將輸入文本分割成單個字符;
  • 計算每對相鄰字符出現(xiàn)的頻率;
  • 找到出現(xiàn)頻率最高的相鄰字符,并將它們合并成一個新的符號;
  • 將新的符號加入到詞匯表中;
  • 更新文本中的所有相鄰字符,用新的符號替換它們;
  • 重復步驟 2-5,直到達到指定的詞匯表大小為止;
  • 將輸入文本分割成 subword(子詞)。

在接下來的章節(jié)中,我們將逐一講解如何實現(xiàn)這些功能。

2.1. 將輸入文本分割成單個字符

我們首先需要將輸入文本分割成單個字符。為此,我們可以使用以下 Java 代碼:

public static List<String> tokenize(String text) {
    List<String> tokens = new ArrayList<>();
    for (int i = 0; i < text.length(); i++) {
        String token = String.valueOf(text.charAt(i));
        tokens.add(token);
    }
    return tokens;
}

接下來,我們可以定義一個示例輸入文本,例如:

String inputText = "hello world";

然后,我們可以調(diào)用 tokenize() 函數(shù)將其分割成單個字符:

List<String> tokens = tokenize(inputText);
System.out.println(tokens);

輸出結(jié)果如下:

[h, e, l, l, o,  , w, o, r, l, d]

現(xiàn)在,我們已經(jīng)成功將輸入文本分割成了單個字符,并將它們保存在了一個 List 中。

2.2. 計算每對相鄰字符出現(xiàn)的頻率

下一步,我們需要計算每對相鄰字符出現(xiàn)的頻率。為此,我們可以遍歷整個文本,記錄每對相鄰字符的出現(xiàn)次數(shù)。

具體來說,我們可以定義一個 Map 變量來存儲每對相鄰字符的出現(xiàn)次數(shù),例如:

Map<String, Integer> charPairsCount = new HashMap<>();
for (int i = 0; i < tokens.size() - 1; i++) {
    String pair = tokens.get(i) + tokens.get(i+1);
    charPairsCount.put(pair, charPairsCount.getOrDefault(pair, 0) + 1);
}
System.out.println(charPairsCount);

輸出結(jié)果如下:

{he=1, el=2, ll=1, lo=1, o =1,  w=1, wo=1, or=1, rl=1, ld=1}

這表示在輸入文本中,字符對“el”出現(xiàn)了 2 次,“ll”和“lo”分別出現(xiàn)了 1 次,等等。

2.3. 找到出現(xiàn)頻率最高的相鄰字符,并將它們合并成一個新的符號

下一步,我們需要找到出現(xiàn)頻率最高的相鄰字符,并將它們合并成一個新的符號。為此,我們可以定義一個函數(shù)來查找最高頻率的字符對:

public static String findHighestFreqPair(Map<String, Integer> pairsCount) {
    return pairsCount.entrySet().stream()
            .max(Map.Entry.comparingByValue())
            .get()
            .getKey();
}

這個函數(shù)會按照字符對出現(xiàn)頻率的降序排列 Map 中的每一項,返回出現(xiàn)頻率最高的字符對。如果兩個字符對的頻率相同,那么會返回最先出現(xiàn)的那個字符對。
接下來,我們可以使用這個函數(shù)來找到出現(xiàn)頻率最高的字符對,并將它們合并成一個新的符號。具體來說,我們可以修改 tokenize() 函數(shù),加入一個詞匯表參數(shù);每次找到最高頻率的字符對后,將它們合并成一個新的符號,并將這個新的符號加入到詞匯表中。

完整代碼如下:

public static List<String> tokenize(String text, Set<String> vocab, int maxVocabSize) {
    List<String> tokens = new ArrayList<>();
    while (true) {
        // 計算每對相鄰字符出現(xiàn)的頻率
        Map<String, Integer> charPairsCount = new HashMap<>();
        for (int i = 0; i < tokens.size() - 1; i++) {
            String pair = tokens.get(i) + tokens.get(i + 1);
            charPairsCount.put(pair, charPairsCount.getOrDefault(pair, 0) + 1);
        }
        // 找到出現(xiàn)頻率最高的相鄰字符對
        String highestFreqPair = findHighestFreqPair(charPairsCount);
        // 如果詞匯表大小已經(jīng)達到指定值,退出循環(huán)
        if (vocab.size() >= maxVocabSize) {
            break;
        }
        // 將最高頻率的字符對合并成一個新的符號
        String[] symbols = highestFreqPair.split("");
        String newSymbol = String.join("", symbols);
        // 將新的符號加入到詞匯表和 token 列表中
        vocab.add(newSymbol);
        tokens = replaceSymbol(tokens, highestFreqPair, newSymbol);
    }
    // 將文本分割成 subwords(子詞)
    List<String> subwords = new ArrayList<>();
    for (String token : tokens) {
        if (vocab.contains(token)) {
            subwords.add(token);
        } else {
            // 如果當前 token 不在詞匯表中,則將其拆分成更小的 subwords
            subwords.addAll(splitToken(token, vocab));
        }
    }
    return subwords;
}

注意,我們在 tokenize() 函數(shù)中增加了一個新的參數(shù) maxVocabSize,用于限制詞匯表的大小。

2.4. 將新的符號加入到詞匯表中

現(xiàn)在,我們已經(jīng)能夠?qū)⒆罡哳l率的字符對合并成一個新的符號,并將這個符號加入到詞匯表和 token 列表中了。假設我們的詞匯表最開始包含單個字符和空格,例如:

Set<String> vocab = new HashSet<>();
vocab.add(" ");
for (char c = 'a'; c <= 'z'; c++) {
    vocab.add(String.valueOf(c));
}

接下來,我們可以調(diào)用 tokenize() 函數(shù)來更新詞匯表:

List<String> tokens = tokenize(inputText, vocab, 10);
System.out.println(tokens);
System.out.println(vocab);

輸出結(jié)果如下:

[h, e, ll, o,  , w, or, ld]
[, , l, d, h, e, ll, o, r, w]

這表示我們成功將輸入文本分割成 subwords,并將其中的字符對“ll”和“or”合并成了新的符號“llor”。該符號已經(jīng)被加入到詞匯表和 token 列表中,同時也被正確地拆分成了兩個 subwords “ll” 和 “or”。

2.5. 更新文本中的所有相鄰字符,用新的符號替換它們

接下來,我們需要更新文本中的所有相鄰字符,用新的符號替換它們。為此,我們可以定義一個 replaceSymbol() 函數(shù),將指定的字符對替換成一個新的符號:

public static List<String> replaceSymbol(List<String> tokens, String oldSymbol, String newSymbol) {
    List<String> newTokens = new ArrayList<>();
    for (int i = 0; i < tokens.size() - 1; i++) {
        String token = tokens.get(i);
        String nextToken = tokens.get(i + 1);
        String pair = token + nextToken;
        if (pair.equals(oldSymbol)) {
            newTokens.add(newSymbol);
            i++; // 跳過下一個字符,因為它已經(jīng)被替換成新的符號了
        } else {
            newTokens.add(token);
        }
    }
    // 處理最后一個字符
    if (!tokens.isEmpty()) {
        newTokens.add(tokens.get(tokens.size() - 1));
    }
    return newTokens;
}

我們可以在 tokenize() 函數(shù)中調(diào)用這個函數(shù),將最高頻率的字符對替換成新的符號:

tokens = replaceSymbol(tokens, highestFreqPair, newSymbol);

2.6. 重復步驟 2-5,直到達到指定的詞匯表大小為止

現(xiàn)在,我們已經(jīng)成功地實現(xiàn)了 BPE 算法中的核心部分。接下來,我們只需要在 tokenize() 函數(shù)中添加循環(huán),重復執(zhí)行步驟 2-5,直到達到指定的詞匯表大小為止即可。

完整的代碼如下:

public static List<String> tokenize(String text, Set<String> vocab, int maxVocabSize) {
    List<String> tokens = new ArrayList<>();
    while (true) {
        // 計算每對相鄰字符出現(xiàn)的頻率
        Map<String, Integer> charPairsCount = new HashMap<>();
        for (int i = 0; i < tokens.size() - 1; i++) {
            String pair = tokens.get(i) + tokens.get(i + 1);
            charPairsCount.put(pair, charPairsCount.getOrDefault(pair, 0) + 1);
        }
        // 找到出現(xiàn)頻率最高的相鄰字符對
        String highestFreqPair = findHighestFreqPair(charPairsCount);
        // 如果詞匯表大小已經(jīng)達到指定值,退出循環(huán)
        if (vocab.size() >= maxVocabSize) {
            break;
        }
        // 將最高頻率的字符對合并成一個新的符號
        String[] symbols = highestFreqPair.split("");
        String newSymbol = String.join("", symbols);
        // 將新的符號加入到詞匯表和 token 列表中
        vocab.add(newSymbol);
        tokens = replaceSymbol(tokens, highestFreqPair, newSymbol);
    }
    // 將文本分割成 subwords(子詞)
    List<String> subwords = new ArrayList<>();
    for (String token : tokens) {
        if (vocab.contains(token)) {
            subwords.add(token);
        } else {
            // 如果當前 token 不在詞匯表中,則將其拆分成更小的 subwords
            subwords.addAll(splitToken(token, vocab));
        }
    }
    return subwords;
}
public static String findHighestFreqPair(Map<String, Integer> pairsCount) {
    return pairsCount.entrySet().stream()
            .max(Map.Entry.comparingByValue())
            .get()
            .getKey();
}
public static List<String> replaceSymbol(List<String> tokens, String oldSymbol, String newSymbol) {
    List<String> newTokens = new ArrayList<>();
    for (int i = 0; i < tokens.size() - 1; i++) {
        String token = tokens.get(i);
        String nextToken = tokens.get(i + 1);
        String pair = token + nextToken;
        if (pair.equals(oldSymbol)) {
            newTokens.add(newSymbol);
            i++; // 跳過下一個字符,因為它已經(jīng)被替換成新的符號了
        } else {
            newTokens.add(token);
        }
    }
    // 處理最后一個字符
    if (!tokens.isEmpty()) {
        newTokens.add(tokens.get(tokens.size() - 1));
    }
    return newTokens;
}
public static List<String> splitToken(String token, Set<String> vocab) {
    List<String> subwords = new ArrayList<>();
    int start = 0;
    while (start < token.length()) {
        // 找到最長的當前詞庫中存在的 subword
        int end = token.length();
        while (start < end) {
            String sub = token.substring(start, end);
            if (vocab.contains(sub)) {
                subwords.add(sub);
                break;
            }
            end--;
        }
        // 如果沒有找到任何 subword,則將當前字符作為 subword 處理
        if (end == start) {
            subwords.add(String.valueOf(token.charAt(start)));
            start++;
        } else {
            start = end;
        }
    }
    return subwords;
}

現(xiàn)在,我們已經(jīng)完成了 BPE 算法的 Java 實現(xiàn)。接下來,我們可以使用以下代碼測試該實現(xiàn):

public static void main(String[] args) {
    // 定義詞匯表和輸入文本
    Set<String> vocab = new HashSet<>();
    vocab.add(" ");
    for (char c = 'a'; c <= 'z'; c++) {
        vocab.add(String.valueOf(c));
    }
    String inputText = "hello world";
    // 將輸入文本分割成 subwords
    List<String> subwords = tokenize(inputText, vocab, 10);
    // 輸出結(jié)果
    System.out.println(subwords);
    System.out.println(vocab);
}

輸出結(jié)果如下:

[h, e, ll, o,  , w, or, ld]
[, , l, d, h, e, ll, o, r, w]

這表明詞匯表已經(jīng)被擴充到了 10 個符號,輸入文本也被成功地分割成了 subwords。同時,我們也可以看到我們新添加的符號“llor”已經(jīng)被正確地拆分成了“ll”和“or”兩個 subwords。

該算法可以用于處理文本分詞的問題。但需要注意的是,BPE 算法是一種無監(jiān)督學習算法,因此在應用時可能會遭遇一些困難。

例如,在使用 BPE 算法時,我們需要指定一個固定大小的詞匯表(即最多包含多少個符號),但這并不總是容易做到。如果詞匯表設置得太小,那么某些單詞就無法表示為 subwords;而如果詞匯表設置得太大,那么我們最終得到的 subwords 可能會過于小,從而失去了原始文本中的有意義的單詞和短語。

另外,BPE 算法也存在一些其他的限制和缺陷。例如,它不能很好地處理一些特殊字符,如 URL、電子郵件地址、日期等。此外,如果兩個 subwords 合并后得到的新 subword 在訓練數(shù)據(jù)中沒有出現(xiàn)過,那么 BPE 算法不能正確地處理這種情況。

盡管存在一些限制和缺陷,BPE 算法仍然被廣泛應用于自然語言處理領(lǐng)域,并被認為是構(gòu)建神經(jīng)機器翻譯和語言模型的有效工具之一。

以上就是ChatGpt都使用的Java BPE分詞算法不要了解一下的詳細內(nèi)容,更多關(guān)于Java BPE分詞算法的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 利用session實現(xiàn)簡單購物車功能

    利用session實現(xiàn)簡單購物車功能

    這篇文章主要為大家詳細介紹了利用session實現(xiàn)簡單購物車功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • SpringBoot動態(tài)導出word文檔實整教程(復制即可使用)

    SpringBoot動態(tài)導出word文檔實整教程(復制即可使用)

    在我們做項目的時候會需要把數(shù)據(jù)庫中的數(shù)據(jù)導出到word當中,下面這篇文章主要給大家介紹了關(guān)于SpringBoot動態(tài)導出word文檔實整教程的相關(guān)資料,文中的代碼復制即可使用,需要的朋友可以參考下
    2023-06-06
  • log4j.properties 配置(實例講解)

    log4j.properties 配置(實例講解)

    下面小編就為大家?guī)硪黄猯og4j.properties 配置(實例講解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • Java實現(xiàn)評論回復功能的完整步驟

    Java實現(xiàn)評論回復功能的完整步驟

    這篇文章主要給大家介紹了關(guān)于Java實現(xiàn)評論回復功能的完整步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • 基于EasyExcel實現(xiàn)百萬級數(shù)據(jù)導入導出詳解

    基于EasyExcel實現(xiàn)百萬級數(shù)據(jù)導入導出詳解

    大數(shù)據(jù)的導入和導出,相信大家在日常的開發(fā)、面試中都會遇到。本文將為大家詳細介紹一下如何利用EasyExcel實現(xiàn)百萬級數(shù)據(jù)導入導出,需要的可以參考一下
    2023-01-01
  • java wait()/notify() 實現(xiàn)生產(chǎn)者消費者模式詳解

    java wait()/notify() 實現(xiàn)生產(chǎn)者消費者模式詳解

    這篇文章主要介紹了java wait()/notify() 實現(xiàn)生產(chǎn)者消費者模式詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 使用MQ消息隊列的優(yōu)缺點詳解

    使用MQ消息隊列的優(yōu)缺點詳解

    這篇文章主要介紹了使用MQ消息隊列的優(yōu)缺點詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-07-07
  • SpringBoot詳解整合Redis緩存方法

    SpringBoot詳解整合Redis緩存方法

    本文主要介紹了SpringBoot整合Redis緩存的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • 詳解如何在SpringBoot中使用WebMvc

    詳解如何在SpringBoot中使用WebMvc

    Spring?Boot?是一個快速、簡單的開發(fā)框架,在?Spring?Boot?中,我們可以使用?WebMvc?來構(gòu)建?Web?應用程序,所以本文就來講講如何在SpringBoot中使用WebMvc吧
    2023-06-06
  • Java過濾所有特殊字符的案例

    Java過濾所有特殊字符的案例

    這篇文章主要介紹了Java過濾所有特殊字符的相關(guān)資料,包括java中清理所有特殊字符及java正則過濾特殊字符的方法,感興趣的朋友跟隨小編一起看看吧
    2024-02-02

最新評論