显示带有标签的帖子 Vowpal兔子. 显示所有帖子
显示带有标签的帖子 Vowpal兔子. 显示所有帖子

2013年3月15日,星期五

mnist演示

我已经检查了两个演示到的主要分支 威杜布 在里面 演示/ 目录,其中之一是 mnist 基于。该演示练习了由“一反所有”归约构成的神经网络归约。 mnist是规范的神经网络测试数据集,包括从灰度像素表示开始的10位多类分类问题。对于没有完全利用空间结构的方法,现有技术的测试误差约为0.8%,对于利用一切的邪恶集合体的测试误差约为0.25%。使用vee-dub时,对原始像素使用神经网络在mnist上进行训练时的测试错误率为2.2%(在我的台式机的一个核心上花费5分钟),而在mnist上进行训练时的测试错误率为1.1%。 mnist8分钟 (在我的台式机的一个核心上需要一个小时)。

上面的数字是可以的,但不会给任何顽固的神经网络爱好者留下深刻的印象。但是,vee-dub中的神经网络支持并非旨在取代传统特征工程,而是对其进行补充:这是 大声一点 风格。

令人惊讶的是,一点要素工程的效果如何。我已经注意到 n克帮助mnist 但是vee-dub中内置的n-gram支持是针对文本而设计的,因此是一维的。因此我写了一个 小程序 计算垂直,水平和对角线像素n-gram,并将其输入到vee-dub。在mnist上训练时,像素为n-gram的线性模型的测试误差为1.75%,使用3个核进行训练需要1分钟。这些核心中有2个被占用来计算像素n-gram,实际上vee-dub比2个特征提取过程要快,因此在不影响挂钟训练吞吐量的情况下,仍有空间添加一些隐藏单元。仅添加1个隐藏单元(每个班级)可将测试误差降至1.6%,而完全不影响培训时间。在mnist8m上训练像素n-gram线性模型会导致测试误差为1.25%。使用4个核心需要一个小时,其中3个专职用于计算像素n-gram。再次,vee-dub并不是瓶颈,添加5个隐藏单元(每个班级)可使测试误差降至0.9%,而不会影响训练时间。这使vee-dub受到尊重。

在mnist8m上进行培训,尽管对计算的要求更高,但总是有帮助的。 mnist8分钟是通过采用mnist训练集并将其变形的方式构造的,该方式对预测变量进行编码(将其视为利用空间结构),从而对预测变量进行编码。这是一个古老的想法,至少可以追溯到1994年,当时是Abu Mostafa的 提示学习 论文,另外表明可以从未标记的数据构建虚拟示例。虚拟示例是一种成功态度的一部分,它表示1)首先提高模型的复杂性,然后2)担心正则化。还有其他通用的方式进行正则化(例如,装袋,辍学,正确的贝叶斯推断),但虚拟示例可让您编码特定于问题的信息并利用未标记的数据,因此我认为它们很不错。

mnist8分钟数据集由Loosli,Canu和Bottou作为社区服务实现;他们的软件在运行过程中产生了不变的变形,因此虚拟示例可能仍然短暂。这可以很好地映射到vee-dub简化架构,因为可以轻松编写一个简化来从在线实际示例中动态构建临时虚拟示例的简化。

2013年2月21日,星期四

大声一点

我和尼科斯最近 向vee-dub添加了神经网络 通过减少。这不是深度学习的实现,它只是一个隐藏层,因此您可能会问``这有什么意义?''我们最初的动机是仅使用vee-dub赢得一些Kaggle比赛。尽管最近我一直很忙,无法参加任何比赛,但是减少的效果符合预期。特别是,我希望可以通过对线性模型有利的工程特征来继续解决大多数问题,并在末尾添加一些隐藏单元以进一步提高性能。在谈论一个 计算与数据集大小的权衡,但这是一个更明确的示例。

来自的拼接站点数据集 2008年Pascal大规模学习挑战赛 是5000万个标记的DNA序列的集合,每个序列的长度为200个碱基对。就我们的目的而言,这是一个字母有限的字符串的二进制分类问题。这里有些例子:
% paste -d' ' <(bzcat dna_train.lab.bz2) <(bzcat dna_train.dat.bz2) | head -3
-1 AGGTTGGAGTGCAGTGGTGCGATCATAGCTCACTGCAGCCTCAAACTCCTGGGCTCAAGTGATCCTCCCATCTCAGCCTCCCAAATAGCTGGGCCTATAGGCATGCACTACCATGCTCAGCTAATTCTTTTGTTGTTGTTGTTGAGACGAAGCCTCGCTCTGTCGCCCAGGCTGGAGTGCAGTGGCACAATCTCGGCTCG
-1 TAAAAAAATGACGGCCGGTCGCAGTGGCTCATGCCTGTAATCCTAGCACTTTGGGAGGCCGAGGCGGGTGAATCACCTGAGGCCAGGAGTTCGAGATCAGCCTGGCCAACATGGAGAAATCCCGTCTCTACTAAAAATACAAAAATTAGCCAGGCATGGTGGCGGGTGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGT
-1 AAAAGAGGTTTAATTGGCTTACAGTTCCGCAGGCTCTACAGGAAGCATAGCGCCAGCATCTCACAATCATGACAGAAGATGAAGAGGGAGCAGGAGCAAGAGAGAGGTGAGGAGGTGCCACACACTTTTAAACAACCAGATCTCACGAAAACTCAGTCACTATTGCAAGAACAGCACCAAGGGGACGGTGTTAGAGCATT
事实证明,如果将这些字符串分解为$ n $ -grams,则逻辑回归效果很好。这是一个小程序,它将DNA序列处理成4克并输出vee-dub兼容格式。
% less Quaddna2vw.cpp
#include <iostream>
#include <string>

namespace
{
  using namespace std;

  unsigned int
  codec (const string::const_iterator& c)
    {
      return *c == 'A' ? 0 :
             *c == 'C' ? 1 :
             *c == 'G' ? 2 : 3;
    }
}

int
main (void)
{
  using namespace std;

  while (! cin.eof ())
    {
      string line;

      getline (cin, line);

      if (line.length ())
        {
          string::const_iterator ppp = line.begin ();
          string::const_iterator pp = ppp + 1;
          string::const_iterator p = pp + 1;
          unsigned int offset = 1;

          cout << " |f";

          for (string::const_iterator c = p + 1;
               c != line.end ();
               ++ppp, ++pp, ++p, ++c)
            {
              unsigned int val = 64 * codec (ppp) +
                                 16 * codec (pp) +
                                  4 * codec (p) +
                                      codec (c);

              cout << " " << offset + val << ":1";
              offset += 256;
            }

          cout << endl;
        }
    }

  return 0;
}
我将使用以下Makefile来驱动学习渠道。
% less Makefile
SHELL=/bin/zsh
CXXFLAGS=-O3

.SECONDARY:

all:

%.check:
        @test -x "$$(which $*)" || {                            \
          echo "ERROR: you need to install $*" 1>&2;            \
          exit 1;                                               \
        }

dna_train.%.bz2: wget.check
        wget ftp://largescale.ml.tu-berlin.de/largescale/dna/dna_train.$*.bz2

quaddna2vw: Quaddna2vw.cpp

quaddna.model.nn%: dna_train.lab.bz2 dna_train.dat.bz2 Quaddna2vw 大众.check
        time paste -d' '                                        \
            <(bzcat $(word 1,$^))                               \
            <(bzcat $(word 2,$^) | ./quaddna2vw) |              \
          tail -n +1000000 |                                    \
        大众 -b 24 -l 0.05 -自适应 --invariant                 \
          --loss_function logistic -f $@                        \
          $$([ $* -gt 0 ] && echo "--nn $* --inpass")

quaddna.test.%: dna_train.lab.bz2 dna_train.dat.bz2 quaddna.model.% Quaddna2vw 大众.check
        paste -d' '                                             \
          <(bzcat $(word 1,$^))                                 \
          <(bzcat $(word 2,$^) | ./quaddna2vw) |                \
        head -n +1000000 |                                      \
        大众 -t --loss_function logistic -i $(word 3,$^) -p $@

quaddna.perf.%: dna_train.lab.bz2 quaddna.test.% perf.check
        paste -d' '                                             \
          <(bzcat $(word 1,$^))                                 \
          $(word 2,$^) |                                        \
        head -n +1000000 |                                      \
        perf -ROC -APR
这是使用logistic回归对数据进行一次sgd传递的结果。
% make quaddna.perf.nn0
g++ -O3 -I/home/pmineiro/include -I/usr/local/include -L/home/pmineiro/lib -L/usr/local/lib  Quaddna2vw.cpp   -o Quaddna2vw
time paste -d' '                                        \
            <(bzcat dna_train.lab.bz2)                          \
            <(bzcat dna_train.dat.bz2 | ./quaddna2vw) |         \
          tail -n +1000000 |                                    \
        大众 -b 24 -l 0.05 -自适应 --invariant                 \
          --loss_function logistic -f quaddna.model.nn0                 \
          $([ 0 -gt 0 ] && echo "--nn 0 --inpass")
final_regressor = quaddna.model.nn0
Num weight bits = 24
learning rate = 0.05
initial_t = 0
power_t = 0.5
using no cache
Reading from
num sources = 1
average    since         example     example  current  current  current
loss       last          counter      weight    label  predict features
0.673094   0.673094            3         3.0  -1.0000  -0.0639      198
0.663842   0.654590            6         6.0  -1.0000  -0.0902      198
0.623277   0.574599           11        11.0  -1.0000  -0.3074      198
0.579802   0.536327           22        22.0  -1.0000  -0.3935      198
...
0.011148   0.009709     22802601  22802601.0  -1.0000 -12.1878      198
0.009952   0.008755     45605201  45605201.0  -1.0000 -12.7672      198

finished run
number of examples = 49000001
weighted example sum = 4.9e+07
weighted label sum = -4.872e+07
average loss = 0.009849
best constant = -0.9942
total feature number = 9702000198
paste -d' ' <(bzcat dna_train.lab.bz2)   53.69s user 973.20s system 36% cpu 46:22.36 total
tail -n +1000000  3.87s user 661.57s system 23% cpu 46:22.36 total
vw -b 24 -l 0.05 -自适应 --invariant --loss_function logistic -f    286.54s user 1380.19s system 59% cpu 46:22.43 total
paste -d' '                                             \
          <(bzcat dna_train.lab.bz2)                            \
          <(bzcat dna_train.dat.bz2 | ./quaddna2vw) |           \
        head -n +1000000 |                                      \
        大众 -t --loss_function logistic -i quaddna.model.nn0 -p quaddna.test.nn0
only testing
Num weight bits = 24
learning rate = 10
initial_t = 1
power_t = 0.5
predictions = quaddna.test.nn0
using no cache
Reading from
num sources = 1
average    since         example     example  current  current  current
loss       last          counter      weight    label  predict features
0.000020   0.000020            3         3.0  -1.0000 -17.4051      198
0.000017   0.000014            6         6.0  -1.0000 -17.3808      198
0.000272   0.000578           11        11.0  -1.0000  -5.8593      198
0.000168   0.000065           22        22.0  -1.0000 -10.5622      198
...
0.008531   0.008113       356291    356291.0  -1.0000 -14.7463      198
0.008372   0.008213       712582    712582.0  -1.0000  -7.1162      198

finished run
number of examples = 1000000
weighted example sum = 1e+06
weighted label sum = -9.942e+05
average loss = 0.008434
best constant = -0.9942
total feature number = 198000000
paste -d' '                                             \
          <(bzcat dna_train.lab.bz2)                            \
          quaddna.test.nn0 |                                    \
        head -n +1000000 |                                      \
        perf -ROC -APR
APR    0.51482
ROC    0.97749
挂钟训练时间为47分钟,测试APR为0.514。 (如果仔细阅读以上内容,您会注意到我将文件的前一百万行用作测试数据,将其余的几行用作训练数据。)大规模学习挑战的条目的APR约为0.2,这是从unigram logistic回归中得到的,而此数据集上最著名的方法需要多个核心天才能计算并获得约0.58的APR。

在以上运行期间 Quaddna2vw 使用100%的1 cpu和 大众 使用约60%的另一个。换一种说法, 大众 这不是瓶颈,我们可以花一些额外的cpu学习,而不会产生实际的挂钟影响。因此,通过指定少量隐藏单元并通过输入直接连接到输出层,可以大声一点 --nn 8 --inpass。其他所有内容都相同。
% make quaddna.perf.nn8
time paste -d' '                                        \
            <(bzcat dna_train.lab.bz2)                          \
            <(bzcat dna_train.dat.bz2 | ./quaddna2vw) |         \
          tail -n +1000000 |                                    \
        大众 -b 24 -l 0.05 -自适应 --invariant                 \
          --loss_function logistic -f quaddna.model.nn8                 \
          $([ 8 -gt 0 ] && echo "--nn 8 --inpass")
final_regressor = quaddna.model.nn8
Num weight bits = 24
learning rate = 0.05
initial_t = 0
power_t = 0.5
using input passthrough for neural network training
randomly initializing neural network output weights 和 hidden bias
using no cache
Reading from
num sources = 1
average    since         example     example  current  current  current
loss       last          counter      weight    label  predict features
0.600105   0.600105            3         3.0  -1.0000  -0.2497      198
0.576544   0.552984            6         6.0  -1.0000  -0.3317      198
0.525074   0.463309           11        11.0  -1.0000  -0.6047      198
0.465905   0.406737           22        22.0  -1.0000  -0.7760      198
...
0.010760   0.009331     22802601  22802601.0  -1.0000 -11.5363      198
0.009633   0.008505     45605201  45605201.0  -1.0000 -11.7959      198

finished run
number of examples = 49000001
weighted example sum = 4.9e+07
weighted label sum = -4.872e+07
average loss = 0.009538
best constant = -0.9942
total feature number = 9702000198
paste -d' ' <(bzcat dna_train.lab.bz2)   58.24s user 1017.98s system 38% cpu 46:23.54 total
tail -n +1000000  3.77s user 682.93s system 24% cpu 46:23.54 total
vw -b 24 -l 0.05 -自适应 --invariant --loss_function logistic -f    2341.03s user 573.53s system 104% cpu 46:23.61 total
paste -d' '                                             \
          <(bzcat dna_train.lab.bz2)                            \
          <(bzcat dna_train.dat.bz2 | ./quaddna2vw) |           \
        head -n +1000000 |                                      \
        大众 -t --loss_function logistic -i quaddna.model.nn8 -p quaddna.test.nn8
only testing
Num weight bits = 24
learning rate = 10
initial_t = 1
power_t = 0.5
predictions = quaddna.test.nn8
using input passthrough for neural network testing
using no cache
Reading from
num sources = 1
average    since         example     example  current  current  current
loss       last          counter      weight    label  predict features
0.000041   0.000041            3         3.0  -1.0000 -15.2224      198
0.000028   0.000015            6         6.0  -1.0000 -16.5099      198
0.000128   0.000247           11        11.0  -1.0000  -6.7542      198
0.000093   0.000059           22        22.0  -1.0000 -10.7089      198
...
0.008343   0.007864       356291    356291.0  -1.0000 -14.3546      198
0.008138   0.007934       712582    712582.0  -1.0000  -7.0710      198

finished run
number of examples = 1000000
weighted example sum = 1e+06
weighted label sum = -9.942e+05
average loss = 0.008221
best constant = -0.9942
total feature number = 198000000
paste -d' '                                             \
          <(bzcat dna_train.lab.bz2)                            \
          quaddna.test.nn8 |                                    \
        head -n +1000000 |                                      \
        perf -ROC -APR
APR    0.53259
ROC    0.97844
从挂钟的角度来看,这是免费的:总培训时间增加了1秒,并且 大众Quaddna2vw 现在吞吐量大致相等。同时,实际年利率从0.515增加到0.532。这说明了一个基本思想:设计出适合您的线性模型的特征,然后当您精疲力尽时,尝试添加一些隐藏的单元。就像转动设计矩阵 最多十一.

我推测由于自适应梯度导致的学习速率安排,正在发生类似于梯度增强的事情。具体而言,如果直接连接的收敛速度比隐藏单元的收敛速度快,则可以有效地要求它们对线性模型中的残差进行建模。这表明一种更明确的强化形式可能会产生更好的免费午餐。













2012年11月27日,星期二

解开您的乙状结肠

我和Nikos找出了如何将神经网络实现为 威杜布 减少。基本思想是为隐藏单元创建虚假示例,以使核心计算的损失函数的梯度与反向传播相对应。结果非常类似于神经网络学习的迭代加权最小二乘法 华纳和米斯拉。一个区别是,Warner和Misra专注于特定的二阶学习方案,而我们没有指定学习算法,我们只是将vee-dub核心视为GLM求解器,并在线构建增强设计矩阵。这使我们能够利用vee-dub中的不同学习算法,即SGD变体和L-BFGS。在单盒设置中,SGD通常比L-BFGS快,尤其是在vee-dub中实现的增强中。因此,使用L-BFGS娱乐的主要原因是分布式学习。

一些注意事项:
  1. 没有多余的装饰。一个隐藏层,激活功能为tanh,唯一的配置是隐藏单元数。
  2. 具有其他减少量的组合应该可以工作,但未经广泛测试。孤立地,归约进行二进制分类和回归。
  3. 它不是一个完整的狗,但是也不能尽可能快:可以进行更多优化。但是,如果您的问题是高维且稀疏的,则vee-dub的基本基础结构(即解析,哈希等)应在假设密集特征向量的神经网络实现方面取得重大胜利。
  4. 它应该与任何可用的损失函数和所有可用的学习算法变体一起工作。
  5. 二次方和ngram起作用,并应用于输入到隐藏层。

那些乐于解决玩具问题的人会很高兴知道vee-dub现在可以用两个隐藏单元解决3-parity。
pmineiro@pmineirovb-931% ./make-parity 3
-1 |f  1:1 2:-1 3:-1
-1 |f  1:-1 2:1 3:-1
1 |f  1:1 2:1 3:-1
-1 |f  1:-1 2:-1 3:1
1 |f  1:1 2:-1 3:1
1 |f  1:-1 2:1 3:1
-1 |f  1:1 2:1 3:1
1 |f  1:-1 2:-1 3:-1
pmineiro@pmineirovb-932% ./make-parity 3 | ../vowpalwabbit/vw --nn 2 --passes 2000 -k -c --cache_file cache -f model -l 10 --invariant                          final_regressor = model
Num weight bits = 18
learning rate = 10
initial_t = 1
power_t = 0.5
decay_learning_rate = 1
randomly initializing neural network output weights 和 hidden bias
creating cache_file = cache
Warning: you tried to make two write caches.  Only the first one will be made.
Reading from
num sources = 1
average    since         example     example  current  current  current
loss       last          counter      weight    label  predict features
1.550870   1.550870            3         3.0   1.0000  -1.0000        4
1.919601   2.288332            6         6.0   1.0000   0.7762        4
2.011137   2.120980           11        11.0   1.0000  -1.0000        4
2.154878   2.298620           22        22.0   1.0000   0.3713        4
2.354256   2.553635           44        44.0  -1.0000   1.0000        4
2.286332   2.216827           87        87.0  -1.0000   1.0000        4
2.222494   2.158657          174       174.0   1.0000   0.8935        4
1.716414   1.210335          348       348.0  -1.0000  -0.9598        4
1.368982   1.021549          696       696.0   1.0000   0.9744        4
1.151838   0.934694         1392      1392.0   1.0000   1.0000        4
0.976327   0.800816         2784      2784.0   1.0000   1.0000        4
0.756642   0.536958         5568      5568.0   1.0000   1.0000        4
0.378355   0.000000        11135     11135.0  -1.0000  -1.0000        4

finished run
number of examples = 16000
weighted example sum = 1.6e+04
weighted label sum = 0
average loss = 0.2633
best constant = -6.25e-05
total feature number = 64000
pmineiro@pmineirovb-933% ./make-parity 3 | ../vowpalwabbit/vw -i model -t -p /dev/stdout --quiet
-1.000000
-1.000000
1.000000
-1.000000
1.000000
1.000000
-1.000000
1.000000
-q ff,我可以对2个隐藏单位进行4个奇偶校验。哇

更加现实,我尝试 mnist。任务是对手写数字进行多类分类,因此我将“一无所有”归约法与神经网络归约法一起使用。原始像素值是不错的功能,因为数字已尺寸标准化并居中。示例行看起来像
pmineiro@pmineirovb-658% zcat test.data.gz | head -1
 |p 202:0.328125 203:0.72265625 204:0.62109375 205:0.58984375 206:0.234375 207:0.140625 230:0.8671875 231:0.9921875 232:0.9921875 233:0.9921875 234:0.9921875 235:0.94140625 236:0.7734375 237:0.7734375 238:0.7734375 239:0.7734375 240:0.7734375 241:0.7734375 242:0.7734375 243:0.7734375 244:0.6640625 245:0.203125 258:0.26171875 259:0.4453125 260:0.28125 261:0.4453125 262:0.63671875 263:0.88671875 264:0.9921875 265:0.87890625 266:0.9921875 267:0.9921875 268:0.9921875 269:0.9765625 270:0.89453125 271:0.9921875 272:0.9921875 273:0.546875 291:0.06640625 292:0.2578125 293:0.0546875 294:0.26171875 295:0.26171875 296:0.26171875 297:0.23046875 298:0.08203125 299:0.921875 300:0.9921875 301:0.4140625 326:0.32421875 327:0.98828125 328:0.81640625 329:0.0703125 353:0.0859375 354:0.91015625 355:0.99609375 356:0.32421875 381:0.50390625 382:0.9921875 383:0.9296875 384:0.171875 408:0.23046875 409:0.97265625 410:0.9921875 411:0.2421875 436:0.51953125 437:0.9921875 438:0.73046875 439:0.01953125 463:0.03515625 464:0.80078125 465:0.96875 466:0.2265625 491:0.4921875 492:0.9921875 493:0.7109375 518:0.29296875 519:0.98046875 520:0.9375 521:0.22265625 545:0.07421875 546:0.86328125 547:0.9921875 548:0.6484375 572:0.01171875 573:0.79296875 574:0.9921875 575:0.85546875 576:0.13671875 600:0.1484375 601:0.9921875 602:0.9921875 603:0.30078125 627:0.12109375 628:0.875 629:0.9921875 630:0.44921875 631:0.00390625 655:0.51953125 656:0.9921875 657:0.9921875 658:0.203125 682:0.23828125 683:0.9453125 684:0.9921875 685:0.9921875 686:0.203125 710:0.47265625 711:0.9921875 712:0.9921875 713:0.85546875 714:0.15625 738:0.47265625 739:0.9921875 740:0.80859375 741:0.0703125
这是一些结果。 \ [
\ begin {array} {c | c | c}
\ mbox {模型}&\ mbox {测试错误}&\ mbox {注意} \\ \ hline
\ mbox {线性}&\ mbox {848}&\\
\ mbox {Ngram}&\ mbox {436}&\ verb!--ngram 2-跳过1! \\
\ mbox {二次}&\ mbox {301}和\ verb!-q pp! \\
\ mbox {NN}&\ mbox {273}&\ verb!-nn 40!
\ end {array}
\]二次方可提供良好的结果,但训练起来却是s-l-o-w(每个示例都有>10000个功能)。 NGram增强了二次方的功能,并且相当活泼(由于编码,它们是水平n元语法;垂直n元语法也可能会有所帮助)。 40个隐藏单元神经网络的性能优于二次方,并且训练速度也更快。命令行调用如下所示:
pmineiro@pmineirovb-74% ./vw --oaa 10 -l 0.5 --loss_function logistic --passes 10 --hash all -b 22 -自适应 --invariant --random_weights 1 --random_seed 14 --nn 40 -k -c --cache_file nncache train.data.gz -f nnmodel

2011年7月26日,星期二

日志TF

我使用一种编码来解决文本分类问题,该编码在经验上有助于在使用线性分类器(例如 Vowpal兔子 与名义上编码的令牌。换句话说,如果您要输入这样的输入
1 |男孩会男孩
-1 |我来了,我看到了我被征服了
对于Vowpal来说,希望构建一个二进制分类器,您可以做得更好。

在使用logistic损失的情况下,一个是用unigram进行logistic回归,这形成了 生成辨别对 与多项朴素的贝叶斯。因此,如果编码有助于改善朴素的贝叶斯,那么它也可以改善与字母组合的逻辑回归。 伦尼(Rennie)等。等 提出了多项多项式朴素贝叶斯改进,包括对数项频率(TF)编码。为了获得动力,请考虑朴素贝叶斯背后的生成模型,该模型说,以类标签为条件,文档的可能性由\ [
p(D | \ theta)\ propto \ prod_ {w \ in V} \ theta_w ^ {c_w(D)},
\]其中$ V $是词汇量,$ \ theta $是特定于标记的类标签分布,而$ c_w(D)$是文档中标记$ w $的计数。伦尼(Rennie)等。等声称此生成模型的问题在于,相对于凭经验观察到的结果而言,它低估了文档中重复标记的可能性(我同意,已经看到包含由标记``HA''组成的推文重复了11次)。这个问题类似于高斯分布,在最小二乘回归中,离群值的可能性不足。 一种解决方法 用诸如t分布之类的较重的分布代替。他们特别建议\ [
p(D | \ theta)\ propto \ prod_ {w \ in V} \ theta_w ^ {\ log_2(1 + c_w(D))},
\]在结构上是相同的,但使用了计数这一术语。在实践中,我喜欢保留双字扩展的字序,因此我将$ \ log_2(1 + c_w(D))/ c_w(D)$权重分配给输入中每个令牌实例,从而得到
1 |男孩:0.792将会:1是:1男孩:0.792
-1 |我:0.666来了:1我:0.666看到了:1我:0.666被征服了:1
实际上,这可以可靠地提高文本分类性能。他们在该论文中提到的其他一些技巧可以解决某些问题,而不是其他一些问题,因此通常都值得尝试,但是以我的经验,这确实是有帮助的。

日志TF LDA

现在可以进行更多投机活动了。考虑LDA的生成模型,确定每个职位的所有主题分配的条件,然后查看共享相同主题分配的那些职位上单词的分布:这是多项式分布。但是,等等,我们已经确定多项式低估了文档中标记重复的可能性;理所当然地,LDA也遭受同样的问题。我建议只做上面概述的log TF转换;尽管从数学上讲这可能没有意义(结果的可能性与Dirichlet不共轭),但在操作上没有特别的困难。对于变体方法,文档通过单词计数进行汇总,分数计数没有根本问题。对于Gibbs抽样,可以维护对主题的单词分配的各种计数以及每个文档的总数,这仍然可以是分数。

怎么知道最终的模型更好?我认为查看困惑不会有所帮助,因为通过更改令牌计数,可以更改困惑的定义。因此,我将看一下使用LDA的端到端上下文。例如,使用LDA作为监督分类系统的深度学习特征提取器,我将研究log TF LDA是否产生更好的整体分类性能。

2011年6月26日,星期日

更快的LDA:第二部分

我能够提高其他效率 Vowpal兔子的LDA实施 通过 向量化。基本思想是找到重复的昂贵操作的循环并通过 SIMD 同时执行多个昂贵的操作。特别是我正在利用 上证所 指令系统。

向量化范例

对于我的简单功能 近似对数和指数,转换为SSE是一项最直接的机械任务(如此机械,实际上,我想知道为什么编译器无法为我完成此任务?)。这是一个示例:首先,原始的快速以2为底的对数,
// WARNING: this code has been updated.  Do not use this version.
// Instead get the latest version from http://code.google.com/p/fastapprox

inline float
fastlog2 (float x)
{
  union { float f; uint32_t i; } vx = { x };
  union { uint32_t i; float f; } mx = { (vx.i & 0x007FFFFF) | (0x7e << 23) };
  float y = vx.i;
  y *= 1.0f / (1 << 23);

  return
    y - 124.22544637f - 1.498030302f * mx.f - 1.72587999f / (0.3520887068f + mx.f);
}
接下来是SSE版本
// WARNING: this code has been updated.  Do not use this version.
// Instead get the latest version from http://code.google.com/p/fastapprox

typedef float v4sf __attribute__ ((__vector_size__ (16)));
typedef int v4si __attribute__ ((__vector_size__ (16)));
#define v4sf_dup_literal(x) ((v4sf) { x, x, x, x })
#define v4si_dup_literal(x) ((v4si) { x, x, x, x })
#define v4sf_from_v4si __builtin_ia32_cvtdq2ps

inline v4sf
vfastlog2 (v4sf x)
{
  union { v4sf f; v4si i; } vx = { x };
  union { v4si i; v4sf f; } mx = {
    (vx.i & v4si_dup_literal (0x007FFFFF)) | v4si_dup_literal ((0x7e << 23))
  };
  v4sf y = v4sf_from_v4si (vx.i);
  y *= v4sf_dup_literal ((1.0f / (1 << 23)));
  
  const v4sf c_124_22544637 = v4sf_dup_literal (124.22544637f);
  const v4sf c_1_498030302 = v4sf_dup_literal (1.498030302f);
  const v4sf c_1_725877999 = v4sf_dup_literal (1.72587999f);
  const v4sf c_0_3520087068 = v4sf_dup_literal (0.3520887068f);
  
  return 
    y - c_124_22544637 - c_1_498030302 * mx.f - c_1_725877999 / (c_0_3520087068 + mx.f);
}
vfastlog2 运行速度与 fastlog2 (实际上有时会稍快一些,具体取决于 fastlog2 并使用x87运算进行编译),并计算几乎相同的内容(有时差异很小,比近似误差小得多)。但是,它可以一次执行4次计算。

有一个陷阱:它总是一次执行4次计算,因此,如果数组的长度不是16个字节的倍数,则最后一次计算将从内存的其他位读取(可能导致分段错误)。此外,输入必须对齐16字节,否则会出现分段错误。有人告诉我,在超高性能应用程序中,开发人员总是将阵列排列为16字节对齐,并且是16字节长的倍数。但是,没有简单的方法来安排此操作,因此我不得不进行一些防御性的编码。我使用的模式是
// WARNING: this code has been updated.  Do not use this version.
// Instead get the latest version from http://code.google.com/p/fastapprox

typedef float v4sf_aligned __attribute__ ((__vector_size__ (16))) __attribute__ ((aligned (16)));

float f (float);
v4sf vectorized_f (v4sf);

inline void
conservative_map (float* x,
                  size_t n)
{
  unsigned int i;

  // handle non-aligned prefix
  for (i = 0; i < n && (((uintptr_t) (x + i)) % 16) > 0; ++i)
    { 
      x[i] = f (x[i]);
    }

  // the good stuff
  for (; i + 4 < n; i += 4)
    { 
      * (v4sf_aligned *) (x + i) = vectorized_f (* (v4sf_aligned*) (x + i));
    }

  // handle remainder
  for (; i < n; ++i)
    { 
      x[i] = f (x[i]);
    }
}
对于大约100个主题的地图,这在最坏的情况下会导致25个操作之外的6个附加操作,因此平均可能会有10%的罚款。

结果

我在与我的基准相同的基准上进行了一些其他计时测试 以前的帖子。 \ [
\ begin {array} {c | c}
\ mbox {代码版本}&\ mbox {总挂钟} \\ \ hline
\ mbox {原始}&\ mbox {79秒} \\
\ mbox {近似值,无SSE}&\ mbox {49秒} \\
\ mbox {近似值,含SSE}&\ mbox {39秒}
\ end {array}
\]
近似加速比矢量化加速大,这很好,因为即使对于不支持SSE的环境,近似加速始终可用。

2011年6月24日,星期五

更快的LDA

现在,我已经掌握了 字节重新解释的邪恶力量 在整数和IEEE浮点数之间,我一直在寻找加速的东西。最近发布的 快速实施LDA 由Smola等等提醒我看Vowpal Wabbit的 LDA实施。的 官方网站 该文件充满了看起来昂贵的先验功能,可以使用近似版本来加快速度,希望不会破坏学习算法。

更快的Log Gamma和Digamma

除了许多我已经有了快速近似的指数和对数调用之外,还有一些对 对数伽玛地伽玛。幸运的是, 斯特林近似 是对数,因此我可以利用我的快速对数近似值 以前的博客文章.

我发现对于Log Gamma和Digamma而言,两次应用移位公式可以在精度和速度之间取得最佳折衷。
// WARNING: this code has been updated.  Do not use this version.
// Instead get the latest version from http://code.google.com/p/fastapprox

inline float
fastlgamma (float x)
{
  float logterm = fastlog (x * (1.0f + x) * (2.0f + x));
  float xp3 = 3.0f + x;

  return
    -2.081061466f - x + 0.0833333f / xp3 - logterm + (2.5f + x) * fastlog (xp3);
}

inline float
fastdigamma (float x)
{
  float twopx = 2.0f + x;
  float logterm = fastlog (twopx);

  return - (1.0f + 2.0f * x) / (x * (1.0f + x))
         - (13.0f + 6.0f * x) / (12.0f * twopx * twopx)
         + logterm;
}
时间和准确性
时序测试是通过编译完成的 -O3 -finline功能-fast-math,在运行64位Ubuntu Lucid(所以 gcc 4:4.4.3-1ubuntu1libc 2.11.1-0ubuntu7.6)。我还测量了$ x \ in [1/100,10] $的平均相对准确度(使用 lgammaf 来自libc作为Log Gamma的黄金标准,并且 boost :: math :: digamma 作为Digamma的黄金标准)。
fastlgamma relative accuracy = 0.00045967
fastdigamma relative accuracy = 0.000420604
fastlgamma million calls per second = 60.259
lgammaf million calls per second = 21.4703
boost::math::lgamma million calls per second = 1.8951
fastdigamma million calls per second = 66.0639
boost::math::digamma million calls per second = 18.9672
好吧,Log Gamma的速度大约是3倍(但是为什么boost :: math :: lgamma这么慢?)却略好于Digamma的3倍。

LDA时序

所以我将这些近似值放入 官方网站 并使用 wiki1K 来自的文件 测试/训练集/ 目录;但是我将文件本身连接了100次以减慢速度。
原始运行
% (cd test; time ../vw --lda 100 --lda_alpha 0.01 --lda_rho 0.01 --lda_D 1000 -b 13 --minibatch 128 train-sets/wiki1Kx100.dat)
your learning rate is too high, setting it to 1
using no cache
Reading from train-sets/wiki1Kx100.dat
num sources = 1
Num weight bits = 13
learning rate = 1
initial_t = 1
power_t = 0.5
learning_rate set to 1
average    since       example  example    current  current  current
loss       last        counter   weight      label  predict features
10.296875  10.296875         3      3.0    unknown   0.0000       38
10.437156  10.577436         6      6.0    unknown   0.0000       14
10.347227  10.239314        11     11.0    unknown   0.0000       32
10.498633  10.650038        22     22.0    unknown   0.0000        2
10.495566  10.492500        44     44.0    unknown   0.0000      166
10.469184  10.442189        87     87.0    unknown   0.0000       29
10.068007  9.666831        174    174.0    unknown   0.0000       17
9.477440   8.886873        348    348.0    unknown   0.0000        2
9.020482   8.563524        696    696.0    unknown   0.0000      143
8.482232   7.943982       1392   1392.0    unknown   0.0000       31
8.106654   7.731076       2784   2784.0    unknown   0.0000       21
7.850269   7.593883       5568   5568.0    unknown   0.0000       25
7.707427   7.564560      11135  11135.0    unknown   0.0000       39
7.627417   7.547399      22269  22269.0    unknown   0.0000       61
7.583820   7.540222      44537  44537.0    unknown   0.0000        5
7.558824   7.533827      89073  89073.0    unknown   0.0000      457

finished run
number of examples = 0
weighted example sum = 0
weighted label sum = 0
average loss = -nan
best constant = -nan
total feature number = 0
../vw --lda 100 --lda_alpha 0.01 --lda_rho 0.01 --lda_D 1000 -b 13 --minibatc  69.06s user 13.60s system 101% cpu 1:21.59 total
近似运行
% (cd test; time ../vw --lda 100 --lda_alpha 0.01 --lda_rho 0.01 --lda_D 1000 -b 13 --minibatch 128 train-sets/wiki1Kx100.dat)               your learning rate is too high, setting it to 1
using no cache
Reading from train-sets/wiki1Kx100.dat
num sources = 1
Num weight bits = 13
learning rate = 1
initial_t = 1
power_t = 0.5
learning_rate set to 1
average    since       example  example    current  current  current
loss       last        counter   weight      label  predict features
10.297077  10.297077         3      3.0    unknown   0.0000       38
10.437259  10.577440         6      6.0    unknown   0.0000       14
10.347348  10.239455        11     11.0    unknown   0.0000       32
10.498796  10.650243        22     22.0    unknown   0.0000        2
10.495748  10.492700        44     44.0    unknown   0.0000      166
10.469374  10.442388        87     87.0    unknown   0.0000       29
10.068179  9.666985        174    174.0    unknown   0.0000       17
9.477574   8.886968        348    348.0    unknown   0.0000        2
9.020435   8.563297        696    696.0    unknown   0.0000      143
8.482178   7.943921       1392   1392.0    unknown   0.0000       31
8.106636   7.731093       2784   2784.0    unknown   0.0000       21
7.849980   7.593324       5568   5568.0    unknown   0.0000       25
7.707124   7.564242      11135  11135.0    unknown   0.0000       39
7.627207   7.547283      22269  22269.0    unknown   0.0000       61
7.583952   7.540696      44537  44537.0    unknown   0.0000        5
7.559260   7.534566      89073  89073.0    unknown   0.0000      457

finished run
number of examples = 0
weighted example sum = 0
weighted label sum = 0
average loss = -nan
best constant = -nan
total feature number = 0
../vw --lda 100 --lda_alpha 0.01 --lda_rho 0.01 --lda_D 1000 -b 13 --minibatc  41.97s user 7.69s system 102% cpu 48.622 total
因此,从81秒降低到49秒,速度提高了大约40%。但这有效吗?好吧,我让马特(Matt)担任最终裁判(我给他寄了一封修改过的 官方网站 (用于测试),但初步迹象表明,使用昂贵功能的近似版本不会破坏学习过程。

下一步:矢量化

肯定会有更多的速度,因为 官方网站 加载像
for (size_t i = 0; i<global.lda; i++)
    {
      sum += gamma[i];
      gamma[i] = mydigamma(gamma[i]);
    }
只是为了尖叫 向量化。敬请关注!

2011年6月2日,星期四

尺寸分析和梯度下降

考虑根据损失函数$ l $,\ [
\ begin {aligned}
p ^ {(t)}&= w ^ {(t)\ top} x ^ {(t)},\\
w ^ {(t + 1)}&= w ^ {(t)}-\ eta \ frac {\ partial l} {\ partial p} x ^ {(t)}。
\ end {aligned}
\]假设最佳权重向量为$ w ^ * $。如果所有输入均按恒定量$ r $进行缩放,则最佳权重向量为$ w ^ * / r $,如果学习算法生成的输出尊重该身份,那将是很好的选择。沿着这些思路思考的更一般的方法是尺寸分析。

假设预测的维度为\ rho $,输入的维度为\ chi \。那么权重必须具有$(\ rho / \ chi)$的维数才能计算出预测方程。假设损失的大小为$ \ lambda $,这意味着学习率$ \ eta $必须具有$ \ rho ^ 2 / /(\ lambda \ chi ^ 2)$的单位才能计算出更新方程。在实践中,这意味着,如果我们有一个权重向量序列收敛到我们喜欢的某个位置,然后我们更改所有输入以使它们加倍,则学习率必须四分之一,以使生成的权重向量的整个序列将其减半,以使整个预测序列相同。

所以这些想法已经被纳入 Vowpal兔子 (实际上,这就是我意识到它们的方式:我请Vowpal团队帮助我理解我在源代码中看到的内容)。特别是在命令行上指定的$ \ eta $由$ x ^ {(t)\ top} x ^ {(t)} $规范化,对应于$ {1 / / chi ^ 2)$部分。 $ \ eta $维数;但是,这不能解决$ \ rho ^ 2 / \ lambda $部分。 (为什么这么重要?假设我创建了一个新的损失函数,该函数是另一个损失函数的输出的两倍;在命令行上指定的学习率必须降低一半。)

在处理二元模型时,我不得不弄清楚如何对此进行概括,因此我开始考虑它。通过$ x ^ {(t)\ top} x ^ {(t)} $进行归一化肯定会针对输入的全局缩放进行调整,但是如果仅对输入的一个维度进行缩放会怎样?这开始进入以下领域 预处理 这导致了自适应方法 杜契,哈桑和歌手 又名DHS(也同时在 麦克马汉和史特勒 但我将专注于DHS)。在那里他们定义了一个矩阵$ G ^ {{(t)} = \ mathrm {diag} \ left(1 / \ sqrt {\ sum_ {s \ leq t}(g ^ {{s}} _ i} ^ {2}} \ right)$,并使用更新\ [
w ^ {(t + 1)} = w ^ {(t)}-\ eta \ frac {\ partial l} {\ partial p} G ^ {(t)} x ^ {(t)},
\]其中$ g ^ {{s}} =(\ partial l / \ partial p ^ {{s}})x ^ {{s}} $。在此更新中,$ G ^ {((t)} $)的尺寸为$ \ rho /(\ lambda \ chi)\ ldots $越来越近!不幸的是,$ \ eta $仍然不是无量纲的,其尺寸为$ \ rho / \ chi $。请注意,DHS推论1中$ \ eta $的最佳选择与$ \ max_t || w_t-w ^ * || _ {\ infty} $成正比,其单位为$(\ rho / \ chi)$。换句话说,如果所有输入都加倍,我们仍然必须将以前的最佳学习率降低一半才能获得最佳行为。

这就留下了一个问题:单位$(\ rho / \ chi)$可以用来对$ \ eta $进行规范化,使得在命令行上指定的参数是无量纲的。任何与$ t $相关的变化都不在DHS的分析范围之内,但我暂时将其忽略。两件事表明了自己:$ 1 / || x ^ {(t)\ top} x ^ {(t)} || _p $和$ 1 / /(x ^ {(t)\ top} G ^ {(t)} x ^ {(t)})$。 (这些单位为$ 1 / \ chi $,但越来越近了)。它们具有不同的属性。

直观地使自适应学习算法具有优势的是,它们在频繁出现的特征上更加保守,而在很少出现的特征上更具攻击性。使用$ || x ^ {(t)\ top} x ^ {(t)} || _p $归一化,如果遇到一个例子,其中所有特征以前都被广泛地看过和训练过,那么有效学习率将很小。因此,与在训练序列中较早看到此示例相比,预测的变化将很小。相反,通过$ x ^ {(t)\ top} G ^ {(t)} x ^ {(t)} $归一化,如果遇到一个例子,其中所有特征之前都已被广泛了解和训练,则有效学习速率将被归一化以补偿,使得相对于在训练序列中较早看到该示例而言,预测的变化不会很小。另一方面,对于一个在训练序列中较晚出现的新颖特征和频繁特征混合的示例,相对于该示例是否已发生,与采用任一归一化方案的频繁特征权重相比,更新对新颖特征权重的影响将更大。在训练序列中更早。

维度$(\ rho / \ chi)$还有其他内容。自适应学习方法的关键见解之一是使用来自整个输入历史的信息,而不仅仅是当前输入,来驱动学习。到目前为止,总结所有输入的一件事是权重向量。 DHS推论1中的最佳权重向量与$ \ max_t || w_t-w ^ * || _ {\ infty} $成正比,并且由于$ w_0 = 0 $,所以这大约是$ || ww ** ||。 _ \ infty $。 $ w ^ * $的一个近似值(可能太可怕了!)是当前的权重向量。当它为零时,这在开始时是一个特别糟糕的近似值,因此我考虑将提供的$ \ eta $缩放为$ \ max(1,|| w ^ {(t)} || __infty)$,其中仅考虑$ w ^ {(t)} $的具有$ x ^ {(t)} $的相应非零值的分量。

一个实验

有很多想法,所以我决定进行一个实验。我有数以千万计的推文,这些推文根据写该推文的人的性别进行了标记。由于推文中令牌数量的变化,输入向量具有有趣的范数。我使用恒定的学习速率(vowpal按$ x ^ \ top x $进行缩放)和DHS自适应学习算法按不同的值进行缩放进行了训练。我只对数据进行了一次传递,并且报告了训练集上渐进的验证折损。我在命令行上使用$ \ eta = 1 $进行了所有这些测试。 \ [
\ begin {array} {c | c | c}
\ mbox {方法}&\ mbox {损失}&\ mbox {评论} \\ \ hline
\ mbox {最佳常数}&\ mbox {0.722}&\ mbox {在Twitter上,女人比男人多(为什么?)}} \\
\ mbox {非自适应}&\ mbox {0.651}&\ mbox {在这种情况下,大众汽车通过$ || x ^ {(t)} || ^ 2_2 $归一化}} \\
\ mbox {自适应$ || x ^ {(t)} || _1 $规范化}&\ mbox {0.588}&\\
\ mbox {自适应$ || x ^ {(t)} || _2 $规范化}&\ mbox {0.554}和\\
\ mbox {自适应$ || x ^ {(t)} || __infty $归一化}&\ mbox {0.579}&\\
\ mbox {自适应$ x ^ {(t)\ top} G ^ {(t)} x ^ {(t)} $规范化}&\ mbox {0.621}&\ mbox {比替代方案差得多! } \\
\ mbox {自适应$ \ max(1,|| w ^ {(t)} || __infty)$缩放}&\ mbox {0.579}&\\
\ mbox {自适应$ \ max(0.1,|| w ^ {(t)} || __infty)$缩放}&\ mbox {0.562}&\\
\ mbox {自适应$ \ max(1,|| w ^ {(t)} || __infty)$缩放}&\ mbox {0.579}&\ mbox {忽略$ || w ^ {{t )} || __infty $} \\
\ mbox {自适应$ \ max(0.1,|| w ^ {(t)} || __infty)$缩放}&\ mbox {0.560}&\ mbox {忽略$ || w ^ {{t )} || __infty $} \\
\ end {array}
\]
实际上,对于$ z \ leq 0.1 $尝试$ \ max(z,|| w ^ {(t)} || __infty)$会得到相同的结果。我认为这可能是由于常量特征的权重(始终以值1表示;因此,在某种意义上,它具有固定的比例,不会随输入而变化),因此我尝试在计算时不使用常量特征$ || w ^ {(t)} || _ \ infty $,但结果大致相同。

因此,该实验表明,通过自适应范数$ x ^ {(t)\ top} G ^ {(t)} x ^ {(t)} $进行归一化确实会破坏自适应策略的有效性。否则,在此实验的基础上,很难真正偏爱一种策略。话虽如此,我个人最喜欢的是$ \ max(0.1,|| w ^ {(t)} || __ \ infty)$缩放比例,因为它直接来自遗憾分析并具有正确的单位。

现在,我需要回到最初的目标:在训练一个 二元模型.

2011年3月30日,星期三

减少过滤树:Perl实现

最近我一直在用以下方法解决多分类问题 誓言 用一个 机器学习减少。理想情况下,我将使用vowpal提供的减少API在C中对此进行编程。在实践中,vowpal不断变化。因此,为了隔离自己,我一直将vowpal视为我通过IPC与之通信的黑匣子。这种方法有一个缺点:如果我在vowpal内实施减少(基于top的输出),我估计我的总吞吐量至少会大4倍。希望John和他们的工作人员将在不久的将来提供稳定的vowpal减少API。

同时,尽管有点麻烦,但我在这里提出的减少仍然是切实可行的。另外,有时只是看到某种实现可以使这些概念具体化,所以我想在此介绍一下简化的概念。

策略

起点是 过滤树减少 成本敏感的多类分类对重要性加权二元分类的解释。在这种减少中,类别标签被安排为三月疯狂的锦标赛,获胜者扮演获胜者,直到一个类别标签获胜:这就是最终的预测。当两个类别标签``相互竞争''时,真正发生的是重要性加权分类器根据关联实例特征$ x $决定谁获胜。

实际上,我使用的是一种特殊的过滤树 评分过滤树。在这里,重要性加权分类器被限制为\ [
\ Psi _ {\ nu}(x)= 1_ {f(x; \ lambda)> f (x; \phi)}.
\]这里的$ \ lambda $和$ \ phi $是两个“互相比赛”的标签,以查看谁在锦标赛中晋级。该方程式表示的是:
  1. 有一个函数$ f $,它告诉每个类标签实例具有$ x $的性能。
  2. 更好的类别标签总是胜过其他类别标签。
这意味着根据$ f $,比赛的获胜者是最好的球队。这使得$ f $看起来像是一个得分函数(就像从argmax回归中获得的一样),并且基本上可以在测试时忽略锦标赛的结构。在火车上使用比赛是至关重要的,但是对于在嘈杂的问题(即低后悔)上获得良好的表现至关重要。

实施

我假设我们正在尝试在$ | K | $标签之间进行分类,这些标签由整数$ \ {1,\ ldots,| K | \} $表示。我还将假设输入格式与vowpal的本机输入格式非常相似,但是使用的是成本向量而不是标签。 \ [
c_1,\ ldots,c_ {| K |} \; \ textrm {importance} \; \ textrm {tag} | \ textrm {namespace} \; \ textrm {功能} \ ldots
\]因此,例如3类问题输入行可能看起来像\ [
0.7,0.2,1.3 \; 0.6 \; \ textrm {idiocracy} | \ textrm {items} \; \ textrm {hotlatte} \; | \ textrm {desires} \; \ textrm {i} \; \ textrm {like} \; \ textrm {money}
\]最好的选择(最低的费用)是2。

测试时间
应用模型比训练模型更容易理解,所以我将从这里开始。在perl中,我将其转换为一组vowpal输入行,其中每行对应于特定的类标签$ n $,\ [
\; \ textrm {tag} | \ textrm {namespace} n \; \ textrm {功能} \ ldots
\]本质上,成本向量和重要性权重被去除(因为现在没有学习发生),标记被去除(我将单独处理),并且每个命名空间都附加了类标签。由于vowpal使用第一个字母来标识名称空间,因此对名称空间进行操作的选项(例如-q,-ignore)将继续按预期运行。因此,例如继续上面的示例,我们将生成三行\ [
| \ textrm {items} 1 \; \ textrm {hotlatte} \; | \ textrm {desires} 1 \; \ textrm {i} \; \ textrm {like} \; \ textrm {money} \; | \ _1 \; ķ
\] \ [
| \ textrm {items} 2 \; \ textrm {hotlatte} \; | \ textrm {desires} 2 \ ;; \ textrm {i} \; \ textrm {like} \; \ textrm {money} \; | \ _2 \; ķ
\] \ [
| \ textrm {items} 3 \; \ textrm {hotlatte} \; | \ textrm {desires} 3 \; \ textrm {i} \; \ textrm {like} \; \ textrm {money} \; | \ _3 \; ķ
\]这些行中的每行都输入到vowpal,并且具有最低vowpal输出的类别标签被选为比赛的获胜者。命名空间_中的最后一个功能$ k $提供了常量功能的类标签本地化版本,vowpal在每个示例中默默提供了该功能。

火车时间
在火车上,我基本上是参加比赛的:但是由于我知道实际的花费,所以我根据谁``应该赢了''来更新分类器。更新的重要性权重由刚刚参加比赛的两个团队之间的成本绝对差值确定。因此,对于两个类别标签$ i $和$ j $,将有一个训练输入馈入形式为\ [
1 \; \ omega \; \ textrm {tag} | \ textrm {namespace $ i $:1} \; \ textrm {功能} \ ldots | \ textrm {namespace $ j $:-1} \; \ textrm {功能} \ ldots | \ textrm {\ _ $ i $:-1} \; k \; | \ textrm {\ _ $ j $:-1} \; ķ
\]其中$ \ omega = \ textrm {重要性} * \ mbox {abs}(c_i-c_j)$,即,原始重要性权重由实际成本中的绝对差来衡量。在这里,我利用了在名称空间上提供权重的功能,该权重乘以名称空间中所有功能的权重。 (vowpal始终提供的那种讨厌的常量功能呢?它仍然存在并且实际上不应该。正确的处理方法是修补vowpal以接受不提供常量功能的选项。但是我想呈现一些适用于未修补的vowpal的内容,因此我改为输入另一项训练输入,并将所有取反,以使恒定特征保持接近零。)

当一支球队赢了一场比赛,他们本不应该赢,他们仍然在锦标赛中前进。直观地,分类器需要从比赛中先前犯的错误中恢复过来,所以这是正确的做法。

少了什么东西
以下是我要改进的一些内容:
  1. 在vowpal内部实现,而不是通过IPC在外部实现。
  2. 在实现中,我根据特定的班级手动设计比赛。自动构建锦标赛会更好。
  3. 有一种简洁的方法来指定稀疏成本向量会很好。例如,当所有错误同样严重时,所需要做的就是正确标签的标识。
  4. 上述策略不适用于铰链损耗,我也不知道为什么(它似乎适用于平方损耗和逻辑损耗)。可能是我在某个地方犯了一个错误。买者自负!

编码

有两部分:
  • 誓言.pm:这封装了与vowpal的通信。您将需要它来使其正常工作,但主要是无聊的Unix IPC东西。
    • 检测底层vw未能成功启动不是很好(例如,由于尝试加载不存在的模型)。但是,由于挂起,您会注意到这一点。
  • 过滤树:归约实现实际存在的perl脚本。您可以调用它来开始。通常,它与vw本身采用相同的参数,只是将它们传递出去,但有一些例外:
    1. 您必须从标准输入读取数据。我可以截取--data参数并模拟它们,但是我不知道。
    2. 由于前面的语句,您不能使用--passes参数。
    3. 我确实截取了-p参数(用于输出预测),并在归约级别上对此进行了仿真。

您从过滤树看到的输出看起来像来自大众的输出,但事实并非如此。它实际上来自perl脚本,并且被设计为类似于针对多类情况进行适当修改的vw输出。

这是一个示例调用:
% zcat traindata.gz | head -1000 | ./filter-tree -自适应 -l 1 -b 22 --loss_function logistic -f model.users.b22  
average    since       example  example    current  current  current
loss       last        counter   weight      label  predict features
1.000000   1.000000          1      1.0     1.0000   0.0000       16
0.500000   0.000000          2      2.0     1.0000   1.0000       15
0.500000   0.500000          4      4.0     2.0000   1.0000       20
0.375000   0.250000          8      8.0     2.0000   2.0000       19
0.562500   0.750000         16     16.0     5.0000   2.0000       23
0.437500   0.312500         32     32.0     0.0000   1.0000       14
0.281250   0.125000         64     64.0     1.0000   1.0000       16
0.312500   0.343750        128    128.0     0.0000   1.0000       16
0.347656   0.382812        256    256.0     1.0000   1.0000       13
0.322266   0.296875        512    512.0     1.0000   1.0000       20

finished run
number of examples = 1000
weighted examples sum = 1000
average cost-sensitive loss = 0.287
average classification loss = 0.287
best constant for cost-sensitive = 1
best constant cost-sensitive loss = 0.542
best constant for classification = 1
best constant classification loss = 0.542
minimum possible loss = 0.000
confusion matrix
15      1       0       1       0       1       0
77      416     53      23      5       0       1
14      41      281     56      8       3       2
0       0       0       1       0       1       0
0       0       0       0       0       0       0
0       0       0       0       0       0       0
0       0       0       0       0       0       0
-p参数输出制表符分隔的一组列。第一列是预测的类标签,接下来的$ | K | $列是每个类标签的评分函数值,最后一列是实例标签。

通常,源代码(最好)是最好的文档。

过滤树

#! /usr/bin/env perl

use warnings;
use strict;

use 誓言;

$SIG{INT} = sub { die "caught SIGINT"; };

# if this looks stupid it is: these used to be actual class names,
# but i didn't want to release code with the actual class labels that i'm using
use constant {
  ZERO => 0,
  ONE => 1,
  TWO => 2,
  THREE => 3,
  FOUR => 4,
  FIVE => 5,
  SIX => 6, 
};

sub argmin (@)
{
  my (@list) = @_;
  my $argmin = 0;

  foreach my $x (1 .. $#list)
    {
      if ($list[$x] < $list[$argmin])
        {
          $argmin = $x;
        }
    }

  return $argmin;
}

sub tweak_line ($$)
{
  my ($suffix, $rest) = @_;

  $rest =~ s/\|(\S*)/\|${1}${suffix}/g;

  return $rest;
}

sub train_node ($$$$$$$$$)
{
  my ($m, $la, $lb, $pa, $pb, $ca, $cb, $i, $rest) = @_;

  my $argmin = ($ca < $cb) ? -1 : 1;
  my $absdiff = abs ($ca - $cb);

  if ($absdiff > 0)
    {
      chomp $rest;
      my $w = $i * $absdiff;

      my $plusone = 1;
      my $minusone = -1;
      my $chirp = (rand () < 0.5) ? 1 : -1;

      $argmin *= $chirp;
      $plusone *= $chirp;
      $minusone *= $chirp;

      $m->send ("$argmin $w",
                tweak_line ("${la}:$plusone", " |$rest |_ k"),
                tweak_line ("${lb}:$minusone", " |$rest |_ k\n"))->recv ()
      or die "vowpal failed to respond";

      $argmin *= -1;
      $plusone *= -1;
      $minusone *= -1;

      $m->send ("$argmin $w",
                tweak_line ("${la}:$plusone", " |$rest |_ k"),
                tweak_line ("${lb}:$minusone", " |$rest |_ k\n"))->recv ()
      or die "vowpal failed to respond";
   }

  return $pa - $pb;
}

sub print_update ($$$$$$$$)
{
  my ($total_loss, $since_last, $delta_weight, $example_counter, 
      $example_weight, $current_label, $current_predict, 
      $current_features) = @_;

  printf STDERR "%-10.6f %-10.6f %8lld %8.1f   %s %8.4f %8lu\n",
         $example_weight > 0 ? $total_loss / $example_weight : -1,
         $delta_weight > 0 ? $since_last / $delta_weight : -1,
         $example_counter,
         $example_weight,
         defined ($current_label) ? sprintf ("%8.4f", $current_label) 
                                  : " unknown",
         $current_predict,
         $current_features;
}

#---------------------------------------------------------------------
#                                main                                 
#---------------------------------------------------------------------

srand 69;

my @my_argv;
my $pred_fh;

while (@ARGV)
  {
    my $arg = shift @ARGV;
    last if $arg eq '--';

    if ($arg eq "-p")
      {
        my $pred_file = shift @ARGV or die "-p argument missing";
        $pred_fh = new IO::File $pred_file, "w" or die "$pred_file: $!";
      }
    else
      {
        push @my_argv, $arg;
      }
  }

my $model = new 誓言 join " ", @my_argv;

print STDERR <<EOD;
average    since       example  example    current  current  current
loss       last        counter   weight      label  predict features
EOD

my $total_loss = 0;
my $since_last = 0;
my $example_counter = 0;
my $example_weight = 0;
my $delta_weight = 0;
my $dump_interval = 1;
my @best_constant_loss = map { 0 } (ZERO .. SIX);
my @best_constant_classification_loss = map { 0 } (ZERO .. SIX);
my $minimum_possible_loss = 0;
my $classification_loss = 0;
my $mismatch = 0;
my %confusion;

while (defined ($_ = <>))
  {
    my ($preline, $rest) = split /\|/, $_, 2;

    die "bad preline $preline" 
      unless $preline =~ /^([\d\.]+)?\s+([\d\.]+\s+)?(\S+)?$/;

    my $label = $1;
    my $importance = $2 ? $2 : 1;
    my $tag = $3;

    my ($actual_tag, @costs) = split /,/, $tag;

    die "bad tag $tag" unless @costs == 0 || @costs == 8;

    my @scores = 
      map { my $s = $model->send (tweak_line ($_, " |$rest |_ k"))->recv ();
            chomp $s;
            $s
          } (ZERO .. SIX);
    my $current_prediction = argmin @scores;

    if (@costs == 8)
      {
        # it turned out better for my problem to combine classes 6 和 7.
        # costs are already inverted 和 subtracted from 1, so, 
        # have to subtract 1 when doing this
        
        my $class_seven = pop @costs;
        $costs[SIX] += $class_seven - 1;

        # zero level

        my $zero_one = train_node ($model,
                                   ZERO,
                                   ONE,
                                   $scores[ZERO],
                                   $scores[ONE],
                                   $costs[ZERO],
                                   $costs[ONE],
                                   $importance,
                                   $rest) <= 0
                       ? ZERO : ONE;

        my $two_three = train_node ($model,
                                    TWO,
                                    THREE,
                                    $scores[TWO],
                                    $scores[THREE],
                                    $costs[TWO],
                                    $costs[THREE],
                                    $importance,
                                    $rest) <= 0
                        ? TWO : THREE;

        my $four_five = train_node ($model,
                                    FOUR,
                                    FIVE,
                                    $scores[FOUR],
                                    $scores[FIVE],
                                    $costs[FOUR],
                                    $costs[FIVE],
                                    $importance,
                                    $rest) <= 0
                        ? FOUR : FIVE;

        # SIX gets a pass

        # first level: (zero_one vs. two_three), (four_five vs. SIX)

        my $fleft = train_node ($model,
                                $zero_one,
                                $two_three,
                                $scores[$zero_one],
                                $scores[$two_three],
                                $costs[$zero_one],
                                $costs[$two_three],
                                $importance,
                                $rest) <= 0
                    ? $zero_one : $two_three;

        my $fright = train_node ($model,
                                 $four_five,
                                 SIX,
                                 $scores[$four_five],
                                 $scores[SIX],
                                 $costs[$four_five],
                                 $costs[SIX],
                                 $importance,
                                 $rest) <= 0
                     ? $four_five : SIX;

        # second level: fleft vs. fright

        my $root = train_node ($model,
                               $fleft,
                               $fright,
                               $scores[$fleft],
                               $scores[$fright],
                               $costs[$fleft],
                               $costs[$fright],
                               $importance,
                               $rest) <= 0
                   ? $fleft : $fright;

        $total_loss += $importance * $costs[$root];
        $since_last += $importance * $costs[$root];
        $example_weight += $importance;
        $delta_weight += $importance;

        my $best_prediction = argmin @costs;

        foreach my $c (ZERO .. SIX)
          {
            $best_constant_loss[$c] += $importance * $costs[$c];
            if ($c != $best_prediction)
              {
                $best_constant_classification_loss[$c] += $importance;
              }
          }

        $minimum_possible_loss += $importance * $costs[$best_prediction];
        $classification_loss += ($current_prediction == $best_prediction) ? 0 : 1;
        ++$confusion{"$current_prediction:$best_prediction"};

        ++$mismatch if $root ne $current_prediction;
      }

    print $pred_fh (join "\t", $current_prediction, @scores, $actual_tag), "\n"
      if defined $pred_fh;

    ++$example_counter;
    if ($example_counter >= $dump_interval)
      {
        my @features = split /\s+/, $rest;         # TODO: not really

        print_update ($total_loss, 
                      $since_last,
                      $delta_weight,
                      $example_counter,
                      $example_weight,
                      (@costs) ? (argmin @costs) : undef,
                      $current_prediction,
                      scalar @features);

        $dump_interval *= 2;
        $since_last = 0;
        $delta_weight = 0;
      }
  }

my $average_loss = sprintf "%.3f", $example_weight > 0 ? $total_loss / $example_weight : -1;

my $best_constant = argmin @best_constant_loss;
my $best_constant_average_loss = sprintf "%.3f", $example_weight > 0 ? $best_constant_loss[$best_constant] / $example_weight : -1;

my $best_constant_classification = argmin @best_constant_classification_loss;
my $best_constant_classification_average_loss = sprintf "%.3f", $example_weight > 0 ? $best_constant_classification_loss[$best_constant_classification] / $example_weight : -1;

my $minimum_possible_average_loss = sprintf "%.3f", $example_weight > 0 ? $minimum_possible_loss / $example_weight : -1;

my $classification_average_loss = sprintf "%.3f", $example_weight > 0 ? $classification_loss / $example_weight : -1;

print <<EOD;

finished run
number of examples = $example_counter
weighted examples sum = $example_weight
average cost-sensitive loss = $average_loss
average classification loss = $classification_average_loss
best constant for cost-sensitive = $best_constant
best constant cost-sensitive loss = $best_constant_average_loss
best constant for classification = $best_constant_classification
best constant classification loss = $best_constant_classification_average_loss
minimum possible loss = $minimum_possible_average_loss
confusion matrix
EOD
#train/test mismatch = $mismatch

foreach my $pred (ZERO .. SIX)
  {
    print join "\t", map { $confusion{"$pred:$_"} || 0 } (ZERO .. SIX);
    print "\n";
  }

誓言.pm

# 誓言.pm

package 誓言;

use warnings;
use strict;

use POSIX qw (tmpnam mkfifo);
use IO::File;
use IO::Pipe;
use IO::Poll;

sub new ($$)
{
  my $class = shift;
  my $args = shift;

  my $pred_pipename = tmpnam () or die $!;
  my $pred_pipe = mkfifo ($pred_pipename, 0700) or die $!;
  my $pred_fd = POSIX::open ($pred_pipename, 
                             &POSIX::O_RDONLY | 
                             &POSIX::O_NONBLOCK | 
                             &POSIX::O_NOCTTY) or die $!;
  my $pred_fh = new IO::Handle;
  $pred_fh->fdopen ($pred_fd, "r") or die $!;
  POSIX::fcntl ($pred_fh, 
                &POSIX::F_SETFL, 
                POSIX::fcntl ($pred_fh, &POSIX::F_GETFL, 0) & ~&POSIX::O_NONBLOCK);

  my $data_fh = new IO::Pipe or die $!;
  open my $oldout, ">&STDOUT" or die "Can't dup STDOUT: $!";
  eval
    {
      open STDOUT, ">", "/dev/null" or die "Can't redirect STDOUT: $!";
      eval
        {
          open my $olderr, ">&STDERR" or die "Can't dup STDERR: $!";
          eval
            {
              open STDERR, ">", "/dev/null" or die "Can't redirect STDERR: $!";
              $data_fh->writer ("大众 $args -p $pred_pipename --quiet") or die $!;
              $data_fh->autoflush (1);
            };
          open STDERR, ">&", $olderr or die "Can't restore STDERR: $!";
          die $@ if $@;
        };
      open STDOUT, ">&", $oldout or die "Can't restore STDOUT: $!";
      die $@ if $@;
    };
  die $@ if $@;

  my $poll = new IO::Poll;
  $poll->mask ($data_fh => POLLOUT);
  $poll->poll ();
  $poll->remove ($data_fh);
  $poll->mask ($pred_fh => POLLIN);

  my $self = { data_fh => $data_fh,
               pred_fh => $pred_fh,
               pred_file => $pred_pipename,
               poll => $poll,
               args => $args };

  bless $self, $class;
  return $self;
}

sub send ($@)
{
  my $self = shift;

  $self->{'data_fh'}->print (@_);

  return $self;
}

sub recv ($)
{
  my $self = shift;

  $self->{'poll'}->poll ();
  return $self->{'pred_fh'}->getline ();
}

sub DESTROY
{
  my $self = shift;

  $self->{'data_fh'}->close ();
  $self->{'pred_fh'}->close ();
  unlink $self->{'pred_file'};
}

1;

2011年1月2日,星期日

幸福是一条温馨的推文

通常,获得培训标签是完成任务的限制因素。为此, 拉米奇等等 对Twitter进行有趣的观察:推文中包含与Twitter约定相对应的注释。表情符号,#标签和指向提供有关推文的情感,语义和社交内容的信息。

因此,我决定制造一个Twitter幸福探测器。在维基百科的一点帮助下,我了解了 常见表情 用于表示幸福和悲伤。对于任何特定的推文,如果它包含至少一个快乐的图释且不包含悲伤的图释,我称其为“高兴”。如果它包含至少一个悲伤的图释而没有快乐的图释,我就称它为悲伤。否则我称其为模棱两可。大多数推文是模棱两可的,目标是将其概括化。但对于培训,我只会使用明确的推文。

有几个功能 新的誓言兔 在一起使这个问题易于攻击:支持稀疏的高基数功能;通过哈希技巧控制模型复杂度;的 -自适应 标志,就像自动改善的tf-idf加权;对n-gram扩展的本地支持;当然,在午餐结束之前,可以在笔记本电脑上浏览数以千万计的推文的功能。在一周的推文上进行训练并在未来一天的推文上进行测试,得出的AUC为0.87,即,给定随机的快乐和悲伤推文,所得回归器有87%的可能性将快乐推文的得分评为比悲伤推文更高的分数。 (注意:解析时必须删除表情符号,否则,AUC为1。这里的要点是泛化为模棱两可的推文。)在这一点上,我没有利用vowpal中的LDA支持,我只是对每个令牌进行编码名义上,因此大概可以改善这一点。

我从未来进一步抽取了10000条推文的随机样本,结果发现这些推文大多是模棱两可的,因为那是大多数推文的意思。然后,我对它们进行了排名,这是10条最幸福和10条最可悲的推文。只是重申一下,在解析过程中表情符号会从推文中剥离:最幸福和最悲伤的几条推文中都有表情符号,这表明从推文中的其他标记预测表情符号存在的难度。

10个最幸福的推文

@WRiTExMiND no doubt! <--guess who I got tht from? Bwahaha anyway doe I like surprising people it's kinda my thing so ur welcome! And hi :)
@skvillain yeh wiz is dope, got his own lil wave poppin! I'm fuccin wid big sean too he signed to kanye label g.o.o.d music
And @pumahbeatz opened for @MarshaAmbrosius & blazed! So proud of him! Go bro! & Marsha was absolutely amazing! Awesome night all around. =)
Awesome! RT @robscoms: Great 24 hours with nephews. Watched Tron, homemade mac & cheese for dinner, Wii, pancakes & Despicable Me this am!
Good Morning 2 U Too RT @mzmonique718: Morningggg twitt birds!...up 和 getting ready for church...have a good day 和 LETS GO GIANTS!
Goodmorning #cleveland, have a blessed day stay focused 和 be productive 和 thank god for life
AMEN!!!>>>RT @DrSanlare: Daddy looks soooo good!!! God is amazing! To GOD be the glory 和 victory #TeamJesus Glad I serve an awesome God
AGREED!! RT @ILoveElizCruz: Amen to dat... We're some awesome people! RT @itsVonnell_Mars: @ILoveElizCruz gotta love my sign lol
#word thanks! :) RT @Steph0e: @IBtunes HAppy Birthday love!!! =) still a fan of ya movement... yay you get another year to be dope!!! YES!!
Happy bday isaannRT @isan_coy: Selamatt ulang tahun yaaa RT @Phitz_bow: Selamat siangg RT @isan_coy: Slamat pagiiii

10条最悲伤的推文

Migraine, sore throat, cough & stomach pains. Why me God?
Ik moet werken omg !! Ik lig nog in bed en ben zo moe .. Moet alleen opstaan en tis koud buitn :(
I Feel Horrible ' My Voice Is Gone Nd I'm Coughing Every 5 Minutes ' I Hate Feeling Like This :-/
SMFH !!! Stomach Hurting ; Aggy ; Upset ; Tired ;; Madd Mixxy Shyt Yo !
Worrying about my dad got me feeling sick I hate this!! I wish I could solve all these problems but I am only 1 person & can do so much..
Malam2 menggigil+ga bs napas+sakit kepala....badan remuk redam *I miss my husband's hug....#nangismanja#
Waking up with a sore throat = no bueno. Hoping someone didn't get me ill 和 it's just from sleeping. D:
Aaaa ini tenggorokan gak enak, idung gatel bgt bawaannya pengen bersin terus. Calon2 mau sakit nih -___-
I'm scared of being alone, I can't see to breathe when I am lost in this dream, I need you to hold me?
Why the hell is suzie so afraid of evelyn! Smfh no bitch is gonna hav me scared I dnt see it being possible its not!

观察结果


首先,表情符号是通用的,这使得该技巧可用于许多不同的语言(我认为?我看不懂其中的某些语言)。

其次,Twitter数据非常干净。像这样的愚蠢想法从未与Web数据一起使用,因为始终存在将内容与有效负载的其他部分(导航,结构等)分开的巨大障碍。此外,网页很大,而推文很短,因此在网页可能有多个推文的情况下,推文可以具有清晰的情感基调。

第三,vowpal应该在人们遇到文本分类问题时尝试使用的工具列表中。总共花了不到半天的时间,其中大部分是数据争执。

2010年12月29日,星期三

快如闪电的LDA

好吧,我在一家基于Twitter的初创公司有一个新的演出。提示,有一个 誓言 wabbit的新版本 可用,所以我认为我会努力。

新vowpal的热门功能之一是 在线LDA (谢谢 马特·霍夫曼!)。但是Tweets确实很小,因此很自然地要问像LDA这样的模型对于这样的简短文档是否有效。 拉米奇等等 想知道同一件事。
尽管LDA和相关模型在新闻报道和学术摘要中的应用已有很长的历史,但一个开放的问题是,它们是否适用于像Twitter帖子一样短的文档,并且其文本与传统研究的馆藏大不相同–在这里我们发现答案是肯定的。
因此,我抽取了400万条推文样本,对其进行了标记化,然后将其提供给vowpal,并提出了10个主题模型。播放时间:3分钟。除了要注意的是,一条推文平均以11个令牌(即不多)结尾,我将为您省去令牌化的详细信息。

尽管10个主题实在太小了,除了笔触广泛(我只是在热身)之外,什么都没有,但是结果很有趣,所以我想我应该把它粘贴在这里。这是每个主题的前十个标记,每行1个主题。
arenas  carter  villain guiding hoooo   ipods   amir    crazzy   confessions     snort   #awesome
de      la      a       y       que     el      en      no      me     mi      es
the     to      a       my      is      和     in      for     of     you     on
na      ka      sa      ko      ng      mo      ba      ang     ni     pa      wa
di      yg      ga      ada     aja     ya      ini     ke      mau    gw      dan
#fb     alpha   在lantic        2dae    orgy    und     tales   ich    fusion  koolaid creme
ik      de      je      een     en      met     is      in      op     niet    het
maggie  paula   opposition      gems    oiii    kemal   industrial     cancun  ireng   unplug  controllers
9700    t0      bdae    concentration   0ut     day'    armpit  kb     2007    0f      s0
yu      ma      ii      lmaoo   lml     youu    juss    mee     uu     yeaa    ohh
除了成为一个体面的语言检测器之外,该模型还确定了Twitter用户认为很棒的内容(在竞技场中吸食,用ipod欺骗平民)以及人们选择同时选择性地发到Facebook的鸣叫(发牢骚,奶油和koolaid)。

按比例放大,在3500万条推文上运行的100个主题模型在我的笔记本电脑上花费了3小时15分钟。 拉米奇等等 报告称在96个工作日内对800万条推文进行了大约800个主题的LDA模型培训(24台机器为4天)。这不是一个苹果一个苹果,但我认为vowpal中的在线LDA实施要快2到3个数量级。

2010年11月5日,星期五

编码技巧

我用 Vowpal兔子 eHarmony广泛使用,主要用于可伸缩性目的。尽管我们不是Google或Yahoo规模的公司,但我们确实做了一些我们想建模的数十亿次匹配(在一个主要的广告网络中,一到两天就有10亿次展示;因此,在数据方面小3个数量级)。 Vowpal通过在原始线性表示上实现梯度下降的简单性来实现高速,但这实际上意味着人们花时间尝试对原始数据进行非线性变换以提高模型性能。

这是一种广泛适用的编码方案,我已将其用于几个不同的问题,以提高性能。它与 分段回归 (针对统计预测人员)以及 类型2的特殊订单集 (适用于运筹学人员)。我们实际上在办公室将其称为``SOS2编码''。

在一个维度上,它的工作方式如下:为变量选择一组点,然后为任何输入值激活对应于包围该值的点的两个要素,权重对应于两个要素之间的线性插值。例如,对于在线约会,两个人之间的实际距离有助于预测很多兴趣。为了对两个人之间的距离进行编码以输入模型,我首先获取距离的$ \ log_2 $(即席直觉变换),然后使用积分点进行SOS2编码。因此,如果两个人的距离差为72英里,则两个要素输出将为
LOG2_DISTANCE_6:0.83 LOG2_DISTANCE_7:0.19
由于这种编码是稀疏的,因此最终可以与其他变量很好地交互(即,涉及SOS2编码特征的二次vowpal术语会产生有用的提升)。在一个二维问题中,我SOS2对每个维度进行了编码,然后二次成功地进行了交互。对于更高维度的问题,我从其他技术(例如, VQ )。