0x00 сводка
Alink — это платформа алгоритмов машинного обучения нового поколения, разработанная Alibaba на основе вычислительного движка реального времени Flink.Это первая в отрасли платформа машинного обучения, которая поддерживает как пакетные, так и потоковые алгоритмы. Эта и предыдущие статьи помогут вам проанализировать реализацию Word2Vec в Alink.
Поскольку общедоступная информация Alink слишком мала, нижеследующее - все предположения, и обязательно будут упущения и ошибки. Я надеюсь, что все укажут, и я обновлю их в любое время.
0x01 предыдущий обзор
раньшеAlink Talk (16): Word2Vec строит дерево ХаффманаМы узнали о концепции Word2Vec, общей архитектуре в Alink и завершении обработки ввода, а также о создании словарей и бинарных деревьев.
На данный момент у нас уже есть построенное дерево Хаффмана и инициализированные векторы, и мы можем начать вводить текст для обучения.
1.1 Общая блок-схема выше
Сначала дайте общую блок-схему вышеизложенного:
1.2 Обзор деревьев Хаффмана
1.2.1 Определение переменной
Теперь определите переменную следующим образом:
- n : количество слов, содержащихся в контексте слова, которое имеет то же значение, что и n в n-грамме.
- m : длина вектора слова, обычно от 10 до 100
- h : размер скрытого слоя, обычно порядка 100
- N: Размер словаря, обычно 1W~10W
- T : количество слов в обучающем тексте
1.2.2 Зачем вводить дерево Хаффмана
word2vec также использует CBOW и Skip-Gram для обучения модели и получения векторов слов, но не использует традиционную модель DNN. Первая оптимизированная структура данных заключается в использовании дерева Хаффмана для замены нейронов в скрытом слое и выходном слое.Листовые узлы дерева Хаффмана играют роль нейронов в выходном слое, а количество конечных узлов — это словарь. словарного запаса маленький большой. Внутренние узлы действуют как нейроны скрытого слоя.
Взяв в качестве примера CBOW, входной слой представляет собой вектор слов из n-1 слов, длина равна m(n-1), размер скрытого слоя равен h, а размер выходного слоя равен N. Тогда сложность прямого времени равна o(m(n-1)h+hN) = o(hN). Это сложность, необходимая для обработки слова. Если вы хотите обработать весь текст, вам потребуется временная сложность o(hNT). Это неприемлемо.
В то же время мы также заметили, что в o(hNT) относительно фиксированы значения h и T. Если мы хотим их оптимизировать, нам следует в основном начинать с N. Причина, по которой размер выходного слоя равен N, заключается в том, что нейронная сеть должна выполнить задачу выбора 1 из N. Так можно ли уменьшить значение N? Ответ положительный. Решение состоит в том, чтобы разложить одну классификацию на несколько классификаций, что также является основной идеей Hierarchical Softmax.
Например, существует 8 категорий [1, 2, 3, 4, 5, 6, 7, 8]. Если вы хотите определить, к какой категории принадлежит слово А, мы можем идти шаг за шагом. Сначала определите, является ли A принадлежит [1, 2,3,4] по-прежнему принадлежит [5,6,7,8]. Если решено, что он принадлежит к [1,2,3,4], затем проанализируйте, принадлежит ли он к [1,2] или [3,4] и так далее. Таким образом, временная сложность одного слова изменяется с o(hN) сводится к o(hlogN) и, что более важно, снижает нагрузку на память.
От входа к выходу в середине находится древовидная структура, и каждый узел в ней выполняет задачу бинарной классификации (логистической классификации). тогдаЕсть проблема, как построить дерево. Здесь используется дерево Хаффмана, потому что, если оно построено таким образом, путь, пройденный словами с более высокой частотой, будет короче, так что средняя длина пути всех слов будет наименьшей.
Предполагая, что футбольный путь равен 1001, тогда, когда вы хотите вывестифутболПри использовании этого слова модель не выводит напрямую путь «1001», а выполняет бинарную классификацию в каждом узле. Это эквивалентно превращению конечного выходного бинарного дерева в несколько задач бинарной классификации. И каждый корневой узел пути — это искомый вектор. То есть эта модель не только должна найти переменную каждого входного параметра, но также должна найти вектор каждого нелистового узла в двоичном дереве, Конечно, эти векторы являются только временными векторами.
0x02 обучение
2.1 Тренировочный процесс
То, что реализует Alink,Модель Skip-Gram на основе иерархического Softmax.
Теперь давайте посмотрим, как используется Hierarchical Softmax на основе модели Skip-Gram. В это время вводится только одно слово w, а на выходе 2c векторов слов context(w).
Процесс обучения в основном включает три этапа: входной слой (ввод), слой отображения (проекция) и выходной слой (выход).
- Для каждого слова в обучающей выборке само слово используется в качестве входных данных выборки, а первые слова c и последние слова c используются в качестве выходных данных модели Skip-Gram Ожидается, что softmax вероятность эти слова больше, чем другие слова.
- Сначала нам нужно построить словарь в виде дерева Хаффмана (этот шаг был сделан выше).
- Для перехода от входного слоя к скрытому слою (слою отображения) этот шаг проще, чем CBOW, потому что есть только одно слово, поэтому x_w — это вектор слов, соответствующий слову w.
- Обновите наши θwj−1 и x_w по градиентному восхождению.Обратите внимание, что вокруг x_w есть 2c векторов слов, и мы ожидаем, что P (xi | xw), i = 1, 2... 2c будет наибольшим. Здесь мы замечаем, что, поскольку контексты взаимны, ожидая максимизации P(xi|xw), i=1, 2...2c, мы, в свою очередь, также ожидаем P(xw|xi), i=1, 2...2с макс. Так что лучше использовать P(xi|xw) или P(xw|xi), word2vec использует последнее, преимущество этого в том, что в окне итерации мы обновляем не только слово xw, но и xi, i = 1,2...2c всего 2c слов. Таким образом, общая итерация будет более сбалансированной. По этой причине модель Skip-Gram не итеративно обновляет входные данные, как модель CBOW, а итеративно обновляет выходные данные 2c.
- Начиная с корневого узла, значение слоя отображения необходимо непрерывно классифицировать по дереву Хаффмана, а промежуточные векторы и векторы слов постоянно корректировать.
- Предполагая, что вход слоя отображения — это pro(t), а слово — «футбол», то есть w(t) = «футбол», при условии, что его код Хаффмана известен как d(t) = «1001», то согласно коду Хаффмана, от корневого узла до конечного узла "левый и правый, правый и левый", то есть, начиная с корневого узла, сначала поверните налево, затем дважды поверните направо и, наконец, поверните левый.
- Теперь, когда путь известен, промежуточный вектор каждого узла на пути корректируется сверху вниз в соответствии с путем. В первом узле выполняется логистическая классификация на основе промежуточных векторов узла Θ(t,1) и pro(t). Если результат классификации показывает 0, это означает, что классификация неверна (она должна повернуться влево, то есть классифицировать до 1), тогда следует исправить Θ(t, 1) и зафиксировать величину ошибки.
- Далее, после обработки первого узла, начинаем обрабатывать второй узел. Метод аналогичен, исправьте Θ (t, 2) и аккумулируйте сумму ошибки. И так далее для следующих узлов.
- После обработки всех узлов и достижения конечного узла вектор слов v(w(t)) корректируется в соответствии с накопленной ошибкой. Здесь вводится понятие скорости обучения, а η представляет собой скорость обучения. Чем больше скорость обучения, тем больше штраф за ошибочную оценку и тем больше диапазон коррекции для промежуточного вектора.
Таким образом, поток обработки слова w(t) заканчивается. Если в тексте N слов, описанный выше процесс необходимо повторить N раз, начиная с w(0)~w(N-1).
Вот краткое изложение алгоритма модели Skip-Gram на основе Hierarchical Softmax, Итерация градиента использует метод стохастического градиентного восхождения:
- Вход: обучающая выборка корпуса на основе Skip-Gram, размерность вектора слов M, размер контекста Skip-Gram 2c, размер шага η.
- Выходные данные: параметры модели внутреннего узла θ дерева Хаффмана, все векторы слов w
2.2 Создание модели обучения
Значением инициализации промежуточного вектора, хранимого неконечным узлом в дереве Хаффмана, является нулевой вектор, а вектор слова слова, соответствующего листовому узлу, инициализируется случайным образом. Мы видим, что для ввода и вывода будет выполняться AllReduce, то есть агрегировать результаты вычислений каждой задачи.
DataSet <Row> model = new IterativeComQueue()
.initWithPartitionedData("trainData", trainData)
.initWithBroadcastData("vocSize", vocSize)
.initWithBroadcastData("initialModel", initialModel)
.initWithBroadcastData("vocabWithoutWordStr", vocabWithoutWordStr)
.initWithBroadcastData("syncNum", syncNum)
.add(new InitialVocabAndBuffer(getParams()))
.add(new UpdateModel(getParams()))
.add(new AllReduce("input"))
.add(new AllReduce("output"))
.add(new AvgInputOutput())
.setCompareCriterionOfNode0(new Criterion(getParams()))
.closeWith(new SerializeModel(getParams()))
.exec();
2.3 Инициализация словаря и буфера
Класс InitialVocabAndBuffer завершает эту функцию, в основном инициализируя параметры и загружая словарь в память модели. Здесь будет работать только первая итерация.
- Входной массив хранит все векторы слов Vocab, которые являются векторами слов всех листовых узлов дерева Хаффмана. Размер |V|∗|M|, диапазон инициализации [−0,5M, 0,5M], эмпирическое правило.
- Выходной массив хранит параметры Hierarchical Softmax, который представляет собой вектор параметров всех нелистовых узлов дерева Хаффмана (вес между слоем отображения и выходным слоем). Размер |V|∗|M|, инициализирован всеми нулями, эмпирическое правило. На самом деле используется группа |V−1|.
private static class InitialVocabAndBuffer extends ComputeFunction {
Params params;
public InitialVocabAndBuffer(Params params) {
this.params = params;
}
@Override
public void calc(ComContext context) {
if (context.getStepNo() == 1) { // 只有迭代第一次才会运行
int vectorSize = params.get(Word2VecTrainParams.VECTOR_SIZE);
List <Long> vocSizeList = context.getObj("vocSize");
List <Tuple2 <Integer, double[]>> initialModel = context.getObj("initialModel");
List <Tuple2 <Integer, Word>> vocabWithoutWordStr = context.getObj("vocabWithoutWordStr");
int vocSize = vocSizeList.get(0).intValue();
// 生成一个 100 x 12 的input,这个迭代之后就是最终的词向量
double[] input = new double[vectorSize * vocSize];
Word[] vocab = new Word[vocSize];
for (int i = 0; i < vocSize; ++i) {
Tuple2 <Integer, double[]> item = initialModel.get(i);
System.arraycopy(item.f1, 0, input,
item.f0 * vectorSize, vectorSize); //初始化词向量
Tuple2 <Integer, Word> vocabItem = vocabWithoutWordStr.get(i);
vocab[vocabItem.f0] = vocabItem.f1;
}
context.putObj("input", input); // 把词向量放入系统上下文
// 生成一个 100 x 11 的output,就是Hierarchical Softmax的参数
context.putObj("output", new double[vectorSize * (vocSize - 1)]);
context.putObj("vocab", vocab);
context.removeObj("initialModel");
context.removeObj("vocabWithoutWordStr");
}
}
}
2.4 Обновление модели UpdateModel
Здесь выполняется назначение «распределенных вычислений». Среди них, как рассчитать, сколько отправить, на какую задачу/начальную позицию отправки делается в DefaultDistributedInfo. Это должно быть объединено с функцией частей для анализа. Конкретно в [Alink Talk 3] Модель связи AllReduceЕсть подробности. Конкретный расчет выполняется в CalcModel.update.
private static class UpdateModel extends ComputeFunction {
@Override
public void calc(ComContext context) {
List <int[]> trainData = context.getObj("trainData");
int syncNum = ((List <Integer>) context.getObj("syncNum")).get(0);
DistributedInfo distributedInfo = new DefaultDistributedInfo();
long startPos = distributedInfo.startPos(
(context.getStepNo() - 1) % syncNum,
syncNum,
trainData.size()
);
long localRowCnt = distributedInfo.localRowCnt( //计算本分区信息
(context.getStepNo() - 1) % syncNum,
syncNum,
trainData.size()
);
new CalcModel( //更新模型
params.get(Word2VecTrainParams.VECTOR_SIZE),
System.currentTimeMillis(),
Boolean.parseBoolean(params.get(Word2VecTrainParams.RANDOM_WINDOW)),
params.get(Word2VecTrainParams.WINDOW),
params.get(Word2VecTrainParams.ALPHA),
context.getTaskId(),
context.getObj("vocab"),
context.getObj("input"),
context.getObj("output")
).update(trainData.subList((int) startPos, (int) (startPos + localRowCnt)));
}
}
2.5 Обновления вычислений
Обновление расчета выполняется в CalcModel.update.
2.5.1 Приблизительный расчет значения сигмовидной функции
В процессе использования нейросетевых моделей для прогнозирования выборок. Это должно быть предсказано. В настоящее время необходимо использовать сигмовидную функцию. Конкретная форма сигмоидной функции:σ(x)=1 / (1+e^x)
.
σ(x) резко изменяется вблизи x = 0 и постепенно стремится к пологости с обеих сторон, при x > 6 или x
Если предположить, что расчет сигмовидного значения запрашивается каждый раз, это окажет определенное влияние на производительность, когда сигмовидное значение не требует очень строгой точности. Можно использовать приблизительные расчеты. в word2vec. Преобразовать интервал [−6,6] (набор параметровMAX_EXP6) равноудаленно разделены наEXP_TABLE_SIZEРавные части, и значение сигмоиды в каждом интервале вычисляется и сохраняется в массиве expTable. Когда вам нужно его использовать, найдите его непосредственно в массиве.
Реализация в Alink выглядит следующим образом:
public class ExpTableArray {
public final static float[] sigmoidTable = {
0.002473f, 0.002502f, 0.002532f, 0.002562f, 0.002593f, 0.002624f, 0.002655f, 0.002687f, 0.002719f, 0.002751f ......
}
}
2.5.2 Окно и контекст
Context(w) должен брать C слов до и после слова w. Alink должен заранее установить предустановленное окно параметра окна (по умолчанию 5).Каждый раз, когда Context(w) создается, он сначала генерирует [1, окно ] on Случайное целое число C~ , поэтому возьмите C~ слов до и после w, чтобы сформировать Context(w).
if (randomWindow) {
b = random.nextInt(window);
} else {
b = 0;
}
int bound = window * 2 + 1 - b;
for (int a = b; a < bound; ++a) {
.....
}
2.5.3 Обучение
2.5.3.1 Структура данных
существуетc код языкасередина:
- В массиве syn0 хранятся все векторы слов Vocab, которые являются векторами слов всех листовых узлов дерева Хаффмана, то есть весов input -> hidden. Размер |V|∗|M|, диапазон инициализации [−0,5M, 0,5M], эмпирическое правило. В коде это одномерный массив, но его следует понимать как двумерный массив. При доступе это действительно можно увидеть как syn0[i, j] i — это i-е слово, а j — это j-я неявная единица.
- В массиве syn1 хранятся параметры Hierarchical Softmax, который представляет собой вектор параметров всех нелистовых узлов дерева Хаффмана, то есть веса скрытого ----> вывода. Размер |V|∗|M|, инициализирован всеми нулями, эмпирическое правило. На самом деле используется группа |V−1|.
Исходная проблема Softmax приблизительно вырождается в логистические регрессии приблизительно log(K), объединенные в дерево решений.
Группа K θ Softmax теперь становится группой K-1, представляющей нелистовые узлы K-1 бинарного дерева. В Word2Vec он хранится в массиве syn1.
существуетКод ссылкисередина:
- input соответствует syn0, который на приведенном выше рисунке равен v.
- выход соответствует syn1, который на приведенном выше рисунке равен θ.
2.5.3.2 Специальный код
Конкретный код выглядит следующим образом (мы используем метод максимального правдоподобия, чтобы найти вектор слов всех узлов и всех внутренних узлов θ):
private static class CalcModel {
public void update(List <int[]> values) {
double[] neu1e = new double[vectorSize];
double f, g;
int b, c, lastWord, l1, l2;
for (int[] val : values) {
for (int i = 0; i < val.length; ++i) {
if (randomWindow) {
b = random.nextInt(window);
} else {
b = 0;
}
// 在Skip-gram模型中。须要使用当前词分别预測窗体中的词,因此。这是一个循环的过程
// 因为需要预测Context(w)中的每个词,因此需要循环2window - 2b + 1次遍历整个窗口
int bound = window * 2 + 1 - b;
for (int a = b; a < bound; ++a) {
if (a != window) { //遍历时跳过中心单词
c = i - window + a;
if (c < 0 || c >= val.length) {
continue;
}
lastWord = val[c]; //last_word为当前待预测的上下文单词
l1 = lastWord * vectorSize; //l1为当前单词的词向量在syn0中的起始位置
Arrays.fill(neu1e, 0.f); //初始化累计误差
Word w = vocab[val[i]];
int codeLen = w.code.length;
//根据Haffman树上从根节点到当前词的叶节点的路径,遍历所有经过的中间节点
for (int d = 0; d < codeLen; ++d) {
f = 0.f;
//l2为当前遍历到的中间节点的向量在syn1中的起始位置
l2 = w.point[d] * vectorSize;
// 正向传播,得到该编码单元对应的output 值f
//注意!这里用到了模型对称:p(u|w) = p(w|u),其中w为中心词,u为context(w)中每个词, 也就是skip-gram虽然是给中心词预测上下文,真正训练的时候还是用上下文预测中心词, 与CBOW不同的是这里的u是单个词的词向量,而不是窗口向量之和
// 将路径上所有Node连锁起来,累积得到 输入向量与中间结点向量的内积
// f=σ(W.θi)
for (int t = 0; t < vectorSize; ++t) {
// 这里就是 X * Y
// 映射层即为输入层
f += input[l1 + t] * output[l2 + t];
}
if (f > -6.0f && f < 6.0f) {
// 从 ExpTableArray 中查询到相应的值。
f = ExpTableArray.sigmoidTable[(int) ((f + 6.0) * 84.0)];
//@brief此处最核心,loss是交叉熵 Loss=xlogp(x)+(1-x)*log(1-p(x))
//其中p(x)=exp(neu1[c] * syn1[c + l2])/(1+exp(neu1[c] * syn1[c + l2]))
//x=1-code#作者才此处定义label为1-code,实际上也可以是code
//log(L) = (1-x) * neu1[c] * syn1[c + l2] -x*log(1 + exp(neu1[c] * syn1[c + l2]))
//对log(L)中的syn1进行偏导,g=(1 -code - p(x))*syn1
//因此会有
//g = (1 - vocab[word].code[d] - f) * alpha;alpha学习速率
// 'g' is the gradient multiplied by the learning rate
// g是梯度和学习率的乘积
//注意!word2vec中将Haffman编码为1的节点定义为负类,而将编码为0的节点定义为正类,即一个节点的label = 1 - d
g = (1.f - w.code[d] - f) * alpha;
// Propagate errors output -> hidden
// 根据计算得到的修正量g和中间节点的向量更新累计误差
for (int t = 0; t < vectorSize; ++t) {
neu1e[t] += g * output[l2 + t]; // 修改映射后的结果
}
// Learn weights hidden -> output
for (int t = 0; t < vectorSize; ++t) {
output[l2 + t] += g * input[l1 + t]; // 改动映射层到输出层之间的权重
}
}
}
for (int t = 0; t < vectorSize; ++t) {
input[l1 + t] += neu1e[t]; // 返回改动每个词向量
}
}
}
}
}
}
}
2.6 Усреднение
Класс AvgInputOutput усредняет ввод и вывод.
.add(new AllReduce("input"))
.add(new AllReduce("output"))
.add(new AvgInputOutput())
Причина в том, что при выполнении AllReduce оно просто будет накапливаться.Если одновременно запущены задачи context.getNumTask(), то их легко добавить просто и грубо, чтобы значение было расширено на context.getNumTask( ) раз.
private static class AvgInputOutput extends ComputeFunction {
@Override
public void calc(ComContext context) {
double[] input = context.getObj("input");
for (int i = 0; i < input.length; ++i) {
input[i] /= context.getNumTask(); //平均化
}
double[] output = context.getObj("output");
for (int i = 0; i < output.length; ++i) {
output[i] /= context.getNumTask(); //平均化
}
}
}
2.7 Оценка конвергенции
Здесь видно, что сходимость должна определить, достигнуто ли количество итераций.
private static class Criterion extends CompareCriterionFunction {
@Override
public boolean calc(ComContext context) {
return (context.getStepNo() - 1)
== ((List <Integer>) context.getObj("syncNum")).get(0)
* params.get(Word2VecTrainParams.NUM_ITER);
}
}
2.8 Модель сериализации
Это необходимо для завершения операции сериализации в задаче, значение context.getTaskId() которой равно 0, а другие задачи возвращаются напрямую. Здесь собраны результаты расчетов всех задач.
private static class SerializeModel extends CompleteResultFunction {
@Override
public List <Row> calc(ComContext context) {
// 在 context.getTaskId() 为 0 的task中完成序列化操作,其他task直接返回
if (context.getTaskId() != 0) {
return null; //其他task直接返回
}
int vocSize = ((List <Long>) context.getObj("vocSize")).get(0).intValue();
int vectorSize = params.get(Word2VecTrainParams.VECTOR_SIZE);
List <Row> ret = new ArrayList <>(vocSize);
double[] input = context.getObj("input");
for (int i = 0; i < vocSize; ++i) {
// 完成序列化操作
DenseVector dv = new DenseVector(vectorSize);
System.arraycopy(input, i * vectorSize, dv.getData(), 0, vectorSize);
ret.add(Row.of(i, dv));
}
return ret;
}
}
Модель вывода 0x03
Код выходной модели выглядит следующим образом, а функции таковы:
- Свяжите словарь с вычисленным вектором
- Разделите модель на строки по разделу
- отправить модель
model = model
.map(new MapFunction <Row, Tuple2 <Integer, DenseVector>>() {
@Override
public Tuple2 <Integer, DenseVector> map(Row value) throws Exception {
return Tuple2.of((Integer) value.getField(0), (DenseVector) value.getField(1));
}
})
.join(vocab)
.where(0)
.equalTo(0) //把词典和计算出来的向量联系起来
.with(new JoinFunction <Tuple2 <Integer, DenseVector>, Tuple3 <Integer, String, Word>, Row>() {
@Override
public Row join(Tuple2 <Integer, DenseVector> first, Tuple3 <Integer, String, Word> second)
throws Exception {
return Row.of(second.f1, first.f1);
}
})
.mapPartition(new MapPartitionFunction <Row, Row>() {
@Override
public void mapPartition(Iterable <Row> values, Collector <Row> out) throws Exception {
Word2VecModelDataConverter model = new Word2VecModelDataConverter();
model.modelRows = StreamSupport
.stream(values.spliterator(), false)
.collect(Collectors.toList());
model.save(model, out);
}
});
setOutput(model, new Word2VecModelDataConverter().getModelSchema());
3.1 Контактный словарь и вектор
.join(vocab).where(0).equalTo(0)
Он заключается в том, чтобы связать словарь с вычисляемым вектором. Два источника соединения следующие:
// 来源1,计算出来的向量
first = {Tuple2@11501}
f0 = {Integer@11509} 9
f1 = {DenseVector@11502} "0.9371751984171548 0.33341686580829943 0.6472255126130384 0.36692156358000316 0.1187895685629788 0.9223451469664975 0.763874142430857 0.1330720374498615 0.9631811135902764 0.9283700030050634......"
// 来源2,词典
second = {Tuple3@11499} "(9,我们,com.alibaba.alink.operator.batch.nlp.Word2VecTrainBatchOp$Word@1ffa469)"
f0 = {Integer@11509} 9
f1 = "我们"
f2 = {Word2VecTrainBatchOp$Word@11510}
3.2 Разбить модель на строки по разделам
Во-первых, в соответствии с расчетом разбиения модель разбивается на строки. Здесь используются новые возможности java 8 StreamSupport, сплитератор.
Но здесь только способ использования Stream, а не использование его параллельной функции (позже могут быть статьи для изучения).
model.modelRows = StreamSupport
.stream(values.spliterator(), false)
.collect(Collectors.toList());
Например, раздел получает:
model = {Word2VecModelDataConverter@11561}
modelRows = {ArrayList@11562} size = 3
0 = {Row@11567} "胖,0.4345151137723066 0.4923534386513069 0.49497589358976174 0.10917632806760409 0.7007392318076214 0.6468149904858065 0.3804865818632239 0.4348997489483902 0.03362685646645655 0.29769437681180916 0.04287936035337748..."
1 = {Row@11568} "的,0.4347763498886036 0.6852891840621573 0.9862851622413142 0.7061202166493431 0.9896492612656784 0.46525497532250026 0.03379287230189395 0.809333161215095 0.9230387687661015 0.5100444513892355 0.02436724648194081..."
2 = {Row@11569} "老王,0.4337285110643647 0.7605192699353084 0.6638406386520266 0.909594031681524 0.26995654043189604 0.3732722125930673 0.16171135697228312 0.9759668223869069 0.40331291071231623 0.22651841541002585 0.7150087001048662...."
......
3.3 Отправка данных
затем отправьте данные
public class Word2VecModelDataConverter implements ModelDataConverter<Word2VecModelDataConverter, Word2VecModelDataConverter> {
public List <Row> modelRows;
@Override
public void save(Word2VecModelDataConverter modelData, Collector<Row> collector) {
modelData.modelRows.forEach(collector::collect); //发送数据
}
@Override
public TableSchema getModelSchema() {
return new TableSchema( //返回schema
new String[] {"word", "vec"},
new TypeInformation[] {Types.STRING, VectorTypes.VECTOR}
);
}
}
0x04 Вопрос Ответы
Мы упомянули некоторые из вопросов выше, а теперь ответим на них по порядку:
- Какие модули используют возможности распределенной обработки Alink? ответ:
- Разделить слова, подсчитать (чтобы исключить низкочастотные слова, отсортировать);
- сортировка слов;
- тренироваться;
- Какую модель Word2vec реализует Alink? Это модель CBOW или модель со скип-граммом? ответ:
- модель со скип-граммом
- Какой метод оптимизации использует Alink? Является ли Softmax иерархическим? Или отрицательная выборка? ответ:
- Hierarchical Softmax
- Удаляются ли в этом алгоритме стоп-слова? Так называемые стоп-слова — это слова, которые появляются слишком часто, такие как запятые, точки и т. д., так что нет различия. ответ:
- В этой реализации нет места для остановки слов
- Вы использовали адаптивную скорость обучения? ответ:
- нет
0xEE Личная информация
★★★★★★Думая о жизни и технологиях★★★★★★
Публичный аккаунт WeChat:мысли Росси
Если вы хотите получать своевременные новости о статьях, написанных отдельными лицами, или хотите видеть технические материалы, рекомендованные отдельными лицами, обратите внимание.
ссылка 0xFF
Вывод принципа word2vec и анализ кода
Модель представления глубины текста Word2Vec
Принцип word2vec (2) Иерархическая модель на основе Softmax
принцип word2vec (1) основа модели CBOW и Skip-Gram
Принцип модели word2vec (3), основанной на отрицательной выборке
Написать word2vec самостоятельно (1): основные понятия и процессы
Напишите word2vec самостоятельно (2): подсчитайте частоту слов
Напишите word2vec самостоятельно (3): постройте дерево Хаффмана
Напишите сами word2vec (4): CBOW и модель skip-gram
Подробное объяснение математических принципов в Word2vec (1) Содержание и предисловие
Иерархическая модель на основе Softmax
Отрицательные модели на основе выборки
Анализ реализации алгоритма машинного обучения - анализ исходного кода word2vec
Анализ исходного кода Word2Vec
исходные идеи word2vec и ключевые переменные