PolyScribe 0.1.2__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {polyscribe-0.1.2 → polyscribe-0.2.0}/PKG-INFO +1 -1
- {polyscribe-0.1.2 → polyscribe-0.2.0}/include/byte_pair_encoder.hpp +5 -14
- polyscribe-0.2.0/include/typedefs.hpp +18 -0
- {polyscribe-0.1.2 → polyscribe-0.2.0}/python/bindings.cpp +3 -1
- {polyscribe-0.1.2 → polyscribe-0.2.0}/src/byte_pair_encoder.cpp +59 -12
- {polyscribe-0.1.2 → polyscribe-0.2.0}/.gitignore +0 -0
- {polyscribe-0.1.2 → polyscribe-0.2.0}/CMakeLists.txt +0 -0
- {polyscribe-0.1.2 → polyscribe-0.2.0}/LICENSE +0 -0
- {polyscribe-0.1.2 → polyscribe-0.2.0}/README.md +0 -0
- {polyscribe-0.1.2 → polyscribe-0.2.0}/include/word_extracter.hpp +0 -0
- {polyscribe-0.1.2 → polyscribe-0.2.0}/pyproject.toml +0 -0
- {polyscribe-0.1.2 → polyscribe-0.2.0}/src/word_extracter.cpp +0 -0
|
@@ -5,19 +5,9 @@
|
|
|
5
5
|
#include <vector>
|
|
6
6
|
#include <cstdint>
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
struct PairHash {
|
|
10
|
-
inline size_t operator()(const std::pair<int, int>& v) const {
|
|
11
|
-
return v.first * 997 + v.second;
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
using Count = int;
|
|
16
|
-
using Token = int;
|
|
17
|
-
using Pair = std::pair<Token, Token>;
|
|
18
|
-
using PairCounts = std::unordered_map<Pair, Count, PairHash>;
|
|
19
|
-
using WordCounts = std::vector<std::pair<std::vector<Token>, Count>>;
|
|
8
|
+
#include <./typedefs.hpp>
|
|
20
9
|
|
|
10
|
+
namespace Scribe {
|
|
21
11
|
class BytePairEncoder {
|
|
22
12
|
private:
|
|
23
13
|
std::unordered_map<Token, std::vector<uint8_t>> vocab;
|
|
@@ -26,13 +16,14 @@ namespace Scribe {
|
|
|
26
16
|
|
|
27
17
|
std::vector<Token> getCodePoints(const std::string& str);
|
|
28
18
|
Pair getMostFrequentPair(const WordCounts& wordCounts);
|
|
29
|
-
|
|
19
|
+
Pair getBestRankedPair(const WordCounts& wordCounts);
|
|
20
|
+
void doMerge(WordCounts& wordCounts, const Pair& pairToMerge, const int newToken);
|
|
30
21
|
|
|
31
22
|
public:
|
|
32
23
|
BytePairEncoder();
|
|
33
24
|
~BytePairEncoder() = default;
|
|
34
25
|
|
|
35
|
-
void train(const std::string& filename, int cycles, bool verbose);
|
|
26
|
+
void train(const std::string& filename, int cycles, bool normalizedRanking, bool verbose);
|
|
36
27
|
std::vector<Token> encode(const std::string& data);
|
|
37
28
|
std::vector<uint8_t> decode(const std::vector<Token>& tokens);
|
|
38
29
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <unordered_map>
|
|
4
|
+
#include <vector>
|
|
5
|
+
|
|
6
|
+
namespace Scribe {
|
|
7
|
+
struct PairHash {
|
|
8
|
+
inline size_t operator()(const std::pair<int, int>& v) const {
|
|
9
|
+
return v.first * 997 + v.second;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
using Count = int;
|
|
14
|
+
using Token = int;
|
|
15
|
+
using Pair = std::pair<Token, Token>;
|
|
16
|
+
using PairCounts = std::unordered_map<Pair, Count, PairHash>;
|
|
17
|
+
using WordCounts = std::vector<std::pair<std::vector<Token>, Count>>;
|
|
18
|
+
}
|
|
@@ -14,11 +14,13 @@ NB_MODULE(Scribe, m) {
|
|
|
14
14
|
|
|
15
15
|
nb::class_<Scribe::BytePairEncoder>(m, "BytePairEncoder", "A module that applies BPE algorithm on the given text to generate word/sub-word tokens."
|
|
16
16
|
).def(nb::init<>()
|
|
17
|
-
).def("train", &Scribe::BytePairEncoder::train, nb::arg("filename"), nb::arg("cycles"), nb::arg("verbose"),
|
|
17
|
+
).def("train", &Scribe::BytePairEncoder::train, nb::arg("filename"), nb::arg("cycles"), nb::arg("normalizedRanking"), nb::arg("verbose"),
|
|
18
18
|
"Trains the tokenizer on text data contained in `filename` file.\n\n"
|
|
19
19
|
"Args:\n"
|
|
20
20
|
" filename (str): Path to the training data (taken relative from the directory the function will be called in).\n"
|
|
21
21
|
" cycles (int): Number of cycles to train the tokenizer for, total vocab size will be 256 + `cycles`.\n"
|
|
22
|
+
" normalizedRanking (bool): If false, follows default BPE Scoring for token pairs, i.e. P(A|B)."
|
|
23
|
+
" If true, follows WordPiece Scoring for token pairs, i.e. P(A|B)/P(A)*P(B)."
|
|
22
24
|
" verbose (bool): If true, prints log of every merge cycle to standard output.\n"
|
|
23
25
|
"Returns:\n"
|
|
24
26
|
" None"
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include <cstdint>
|
|
8
8
|
#include <fmt/format.h>
|
|
9
9
|
|
|
10
|
+
#include <../include/typedefs.hpp>
|
|
10
11
|
#include "../include/word_extracter.hpp"
|
|
11
12
|
|
|
12
13
|
std::vector<Scribe::Token> Scribe::BytePairEncoder::getCodePoints(const std::string& str) {
|
|
@@ -48,14 +49,57 @@ Scribe::Pair Scribe::BytePairEncoder::getMostFrequentPair(const WordCounts& word
|
|
|
48
49
|
return mostFrequentPair;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
Scribe::Pair Scribe::BytePairEncoder::getBestRankedPair(const WordCounts& wordCounts) {
|
|
53
|
+
PairCounts pairCounts;
|
|
54
|
+
std::unordered_map<Token, Count> tokenCounts;
|
|
55
|
+
double maxScore = 0;
|
|
56
|
+
Pair bestPair = { -1, -1 };
|
|
57
|
+
|
|
58
|
+
for (const auto& [word, count] : wordCounts) {
|
|
59
|
+
int len = word.size();
|
|
60
|
+
if (len < 2) continue;
|
|
61
|
+
|
|
62
|
+
int i = 0, j = 1;
|
|
63
|
+
tokenCounts[word[0]] += count;
|
|
64
|
+
while (j < len) {
|
|
65
|
+
Pair pair = { word[i], word[j] };
|
|
66
|
+
pairCounts[pair] += count;
|
|
67
|
+
tokenCounts[word[j]] += count;
|
|
68
|
+
|
|
69
|
+
i++; j++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (const auto& [word, count] : wordCounts) {
|
|
74
|
+
int len = word.size();
|
|
75
|
+
if (len < 2) continue;
|
|
76
|
+
|
|
77
|
+
int i = 0, j = 1;
|
|
78
|
+
while (j < len) {
|
|
79
|
+
Pair pair = { word[i], word[j] };
|
|
80
|
+
|
|
81
|
+
double score = (double)pairCounts[pair] / tokenCounts[word[i]];
|
|
82
|
+
score /= tokenCounts[word[j]];
|
|
83
|
+
if (score > maxScore) {
|
|
84
|
+
maxScore = score;
|
|
85
|
+
bestPair = pair;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
i++; j++;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return bestPair;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
void Scribe::BytePairEncoder::doMerge(WordCounts& wordCounts, const Pair& pairToMerge, const int newToken) {
|
|
52
96
|
for (auto& [word, _] : wordCounts) {
|
|
53
97
|
int len = word.size();
|
|
54
98
|
int read_idx = 0;
|
|
55
99
|
int write_idx = 0;
|
|
56
100
|
|
|
57
101
|
while (read_idx < len) {
|
|
58
|
-
if (read_idx + 1 < len && word[read_idx] ==
|
|
102
|
+
if (read_idx + 1 < len && word[read_idx] == pairToMerge.first && word[read_idx + 1] == pairToMerge.second) {
|
|
59
103
|
word[read_idx] = newToken;
|
|
60
104
|
word[write_idx++] = std::move(word[read_idx]);
|
|
61
105
|
|
|
@@ -81,7 +125,7 @@ Scribe::BytePairEncoder::BytePairEncoder() {
|
|
|
81
125
|
}
|
|
82
126
|
}
|
|
83
127
|
|
|
84
|
-
void Scribe::BytePairEncoder::train(const std::string& filename, int cycles, bool verbose) {
|
|
128
|
+
void Scribe::BytePairEncoder::train(const std::string& filename, int cycles, bool normalizedRanking, bool verbose) {
|
|
85
129
|
WordExtracter extracter;
|
|
86
130
|
|
|
87
131
|
std::unordered_map<std::string, Count> strWordCounts = extracter.wordify(filename);
|
|
@@ -97,9 +141,12 @@ void Scribe::BytePairEncoder::train(const std::string& filename, int cycles, boo
|
|
|
97
141
|
wordCounts.emplace_back(std::move(initialTokens), count);
|
|
98
142
|
}
|
|
99
143
|
|
|
144
|
+
Pair pairToMerge;
|
|
100
145
|
for (int i = 0; i < cycles; i++) {
|
|
101
|
-
|
|
102
|
-
|
|
146
|
+
if (!normalizedRanking) pairToMerge = getMostFrequentPair(wordCounts);
|
|
147
|
+
else pairToMerge = getBestRankedPair(wordCounts);
|
|
148
|
+
|
|
149
|
+
if (pairToMerge == Pair(-1, -1)) {
|
|
103
150
|
std::cerr << "\n\n[INFO]::BYTE_PAIR_ENCODER::CYCLES: Max possible vocab size reached, ";
|
|
104
151
|
std::cerr << "{Cycles: " << cycles << "} is too high for the provided dataset.";
|
|
105
152
|
std::cerr << "\nFinishing gracefully at cycle: " << i << "." << std::endl;
|
|
@@ -107,20 +154,20 @@ void Scribe::BytePairEncoder::train(const std::string& filename, int cycles, boo
|
|
|
107
154
|
}
|
|
108
155
|
|
|
109
156
|
int newToken = i + 256;
|
|
110
|
-
doMerge(wordCounts,
|
|
157
|
+
doMerge(wordCounts, pairToMerge, newToken);
|
|
111
158
|
|
|
112
|
-
mergeForest.emplace_back(
|
|
159
|
+
mergeForest.emplace_back(pairToMerge, newToken);
|
|
113
160
|
|
|
114
|
-
std::vector<uint8_t> newBytes = vocab[
|
|
115
|
-
newBytes.insert(newBytes.end(), vocab[
|
|
161
|
+
std::vector<uint8_t> newBytes = vocab[pairToMerge.first];
|
|
162
|
+
newBytes.insert(newBytes.end(), vocab[pairToMerge.second].begin(), vocab[pairToMerge.second].end());
|
|
116
163
|
vocab[newToken] = newBytes;
|
|
117
164
|
|
|
118
165
|
if (!verbose) continue;
|
|
119
166
|
std::string w1 = "", w2 = "";
|
|
120
|
-
for (auto ch : vocab[
|
|
121
|
-
for (auto ch : vocab[
|
|
167
|
+
for (auto ch : vocab[pairToMerge.first]) w1 += static_cast<char>(ch);
|
|
168
|
+
for (auto ch : vocab[pairToMerge.second]) w2 += static_cast<char>(ch);
|
|
122
169
|
std::string w3 = w1 + w2;
|
|
123
|
-
std::clog << fmt::format("[INFO]::NEW_TOKEN_{:<6}: Merged Pair {{{:^6}, {:^6}}} : {{{:^15}, {:^15}}} -> {:<40}", newToken,
|
|
170
|
+
std::clog << fmt::format("[INFO]::NEW_TOKEN_{:<6}: Merged Pair {{{:^6}, {:^6}}} : {{{:^15}, {:^15}}} -> {:<40}", newToken, pairToMerge.first, pairToMerge.second, w1, w2, w3) << std::endl;
|
|
124
171
|
}
|
|
125
172
|
}
|
|
126
173
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|