NlpFoundation
1、基础
如何将计算机不认识的文本特征转化为数字特征
常见的特征分类有类别特征(categorical feature)和数值特征(numeric feature)。 类别特征一般是有限集合, 没有大小之分, 是并列权重。数值特征如:年龄, 工资等, 有数值大小之分。计算机只能处理数值型特征,因此需要将非数值特征转化为计算机能识别的数字, 如下表中的gender和nationality。gender为二元特征, nationality为多元的类别特征。
国籍表示成1-197之间的整数(全球大概有197个国家), 但这些整数只是一个类别, 它们之间无法比较大小, 因为这些整数只是类别而已, 并不是真正的数值, 所以需要对国籍做one-hot Embedding。需要注意的是我们这里用数字表示的类别特征从1开始, 因为0要用来保留未知或缺失的国籍,数据库中经常会有缺失数据, 这些缺失的国籍就用0来表示。
对于性别, 用0表示女性, 1表示男性。
上面提到, 1-197只代表一个类别, 它们之间无大小之分, 因此需要对国籍进一步做one-hot Embedding。
类别特征转化数字流程
上面我们将每个国籍与一个数字进行了映射, 因此会得到这样的一个字典{‘US’-1, ‘China’:2, ‘India’:3, ‘German’: 4,…}, 针对197种国籍, 每个用数字代表的国籍又可以映射为197×1的向量, 使用上面的字典查找国籍的index(如index(China) = 2), 则该197×1的向量的第二维为1, 其他为0。
1 | "US":1->[1, 0, 0, 0,...] |
nationality转成one-hot向量后得到的表格如下。
针对表格中人的三个特征(age, gender, nationality)转化为数字后, 可以得到1+1+197=199维的向量,向量中每个数值的含义如下图。
1 | (35, Male, Chine) = (35, 1, 0, 1, 0, ...) |
为什么要使用one-hot向量来表示呢,
如果不使用one-hot向量而用1, 2, 3, …分别代表US, China, India…, 则在此列运算时,”US” + “China” = “India”, 这显然不合理, 而用one-hot向量表示时, “US” + “China” = [1, 0, 0, …] + [0, 1, 0, …] = [1, 1, 0,…]。 向量[1, 1, 0, 0…]表示既包含US也包好China的类别信息, 可以更好的表示二者类别特征相加的意思,显然one-hot表示更为合理。
文本特征处理
在自然语言处理中, 数字都是文本, 文本可以分割成很多单词, 需要把单词表示成数值向量, 每个单词就是一个类别, 如果字典中有1万个单词, 那么就有1万个类别。很显然, 单词就是categorical features, 用categorical features的方法把单词变成数值向量。
文本处理的第一步就是把文本处理成单词, 一段话、一篇文章或一本书都可以表示成一个字符串, 可以把文本分成很多单词,这个操作叫作tokenization。
第一步:文本->单词: 分词。 tokenization就是把文本变成单词列表。
1 | before: |
第二步: 计算词频, 也就是每个单词出现的次数, 可以用一个hash表来计数, 开始时hash表是空的, 然后按照下面的方式更新hash表。
如果单词w不在hash表中, 这就说明到目前为止文本中只出现过一次单词w, 所以把w加入hash表中, 让它的词频等于1。假如单词w在hash表中, 说明之前文本中就已经出现过单词w了, 需要将它的词频加1。
1 | 总结如下: |
如下图所示
第三步: 排序, 将字典中的词频从大到小排序
表中最前面的是词频最高的词, 表最后是词频最低的。
然后把词频换成index, 转化完成的hash表称为词汇表vocabulary。词频最高的index是1。
统计词频的目的是保留常用词, 去掉词频词。比如你可以设置保存最常用的前10k个单词。为什么要去掉地频词呢, 有如下三点原因。
1、很多地频词是name entities, 在大多数的应用中, name entities没有任何意义。
2、拼写过程难免出现错误, 低频词还有可能是拼写错误造成的。
3、不希望词汇表vocabulary太大, vocabulary越大, one-hot向量维度就越高。
第一步的tokenization把文本分割成单词的列表, 第二步建立了一个字典(也可为hash表), 将每个单词映射到一个正整数。下面开始第三步, 对单词做one-hot encoding。
如果有需要的话, 可以将每个index转换成一个one-hot向量。one-hot向量的维度就是字典vocabulary的维度。
上面提到, 字典中的低频词会被删除, 所以有些词在字典中找不到。假如有个错误拼写的单词bi, 这个单词在字典中找不到, 做one-hot encoding时可以忽略这些词, 也可以将其编码为0.
2、文本处理与词嵌入
应用的数据集是IMDB(数据集下载地址)](http://ai.stanford.edu/~amaas/data/sentiment/))电影评论数据集, 该数据集包含了5万条偏向明显的评论, 其中2.5万条作为训练集, 2.5万条作为测试机。label为pos和neg,是一个二分类问题。
1、文本变成序列, text to sequence, 也就是tokenization, 把文本分割成单词, 一个token就是一个单词, 有些应用中, token可以是一个符号。
会有很多考虑, 1、是否将大写变成小写, 正常情况下, 大小写意义相同, 但有些特例,比如Apple表示苹果公司, 而apple表示水果。2、有些应用会去掉停用词stop word。 3、会存在拼写错误的情况, 因此需要typo correction文本纠错。
2、建立字典。首先统计词频, 去掉地频词, 然后每个单词对应一个正整数。 有了字典, 可以将每个单词映射成一个整数, 这样一句话可以用正整数的列表表示, 这个列表就是sequences。
3、Encoding编码
将字典中的单词映射到索引,索引列表就是一个sequence。如果有必要的话, 还可以进一步做one-hot encoding, 将单词表示成one-hot向量。在IMDB电影评论的例子中, 数据是50k条, 每条电影评论可是表示成一个字符串, tokenization(句子切分成单词列表)之后, 每条电影评论都被转化成一个sequence。
这样的话每一条评论都被转化成一个sequence序列。 注意每个单词的索引是在25k条评论tokenization之后放在一个列表中, 经过排序, 截断之后的索引。排名越靠前, 该单词被使用的频率越高。
但是电影评论有长有短
这样就造成一个问题, 训练数据没有对齐,每条sequence都有不同的长度, 做机器学习训练时需要把数据存储在张量中, 这就要求吧序列对齐, 让每条序列都有相同的长度。
解决方法就是可以固定长度w, 假如这个序列太长, 可切掉前面的单词,当然也可以保留前面的单词。
如果序列太短的话, 就用padding把长度增加到w。
这样处理之后, 所以序列的长度都是w。
对齐之后的sequence就可以存储到一个矩阵中, 文本处理的过程就是每个词用一个正整数来表示, 下一步就是word embedding。
keras中文本处理流程实例。
1 | # !/usr/bin/env python |
以上的过程总结如下:以一条评论为例。
对于测试数据集同样完成tokenization->encoding->alignment这三步。但是需要注意的是, 训练集中的字典与测试集中的字典必须相同, 否则可能会出现同样的单词在测试集中的索引与训练集中的所有不同。
3、Word Embedding词嵌入 word to vector
将单词进一步表示成向量, 之前每个单词都用数字表示, 该如何把这些特征表示成向量呢。显然可以用如下图所示的one-hot编码。
但是如果字典中有1万个单词, 那么这个one-hot维度就太大了, 因此需要做word embedding, 将这些高维向量映射成低维向量。
上图中P是参数矩阵, 它的参数可以在训练过程中从训练数据中学习。ei是字典中第i个单词的one-hot向量。
矩阵P的大小是d×v, d是词向量的维度, 由用户自主定义。矩阵乘法的结果是Xi, Xi就是词向量, 维度是d。
训练好的词向量展示在坐标中, 发现相似的词在坐标中的距离比较近。
keras中提供的Embedding层, 用户需要指定vocabulary的大小和词向量的维度以及每个sequence的长度。
1 | from tensorflow.keras.models import Sequential |
上图中, 处理数据时, 词汇表的大小是10k,以及每个电影评论中保留最后20个词(不足就补齐), 设置词向量的维度等于8。
Embedding层的输出是20x8的句子, 也就是word_num × Embedding_dim
embedding层的参数量等于80k, 80k是这样计算的,Embedding层中有一个参数矩阵p, 矩阵的行数等于vocabulary, 所以矩阵有10k行, 矩阵的列数d是词向量的维度embedding_dim设置为8, 所以矩阵的大小是10k×8=80k, 所以Embedding层一共有80k个参数。
上面已经完成了文本处理和word embed, 每条电影评论保留最后的20个单词, 每个单词用一个8维的词向量表示。现在用logistics regression来做二分类。
Logistic regression for binary classification, 判断电影评论是正面的还是负面的, 用这几行就可实现一个分类器。
1 | from keras.models import Sequential |
下面是编译模型, 然后训练损失函数来拟合模型。
1 | from tensorflow.keras import optimizers |
epochs的意思是把2万条数据扫一遍叫作igepochs, 50个epochs的意思是把训练数据全部扫50遍。
训练得到的准确率和损失曲线如下。
上面的过程总结如下:
针对每条电影评论, 首先进行tokenization, 将电影评论分割成一个一个的单词, 然后把每个单词编码成一个一个数字, 这样一条电影评论就可以用正整数的序列来表示了, 这个正整数的序列叫作sequence, sequence就是神经网络中Embedding的输入。
由于电影评论长短不一, 得到的sequences长短也不一样, 无法存储在一个矩阵中, 解决方法就是alignment对齐, 假设长度大于20个, 就只保留最后20个, 假设长度小于20, 就用0补齐,将长度补到20个, 这样每个sequence都是20个单词长度, 后面就是word embedding.
把长度为20的sequences输入embedding层, embedding层把每个单词映射到8维的词向量, 所以每个长度为20的句子用一个20x8的矩阵来表示
然后用flatten将20x8的矩阵展平, 变成160维的向量, 最后用logistics regression分类器做分类。
embedding层有一个参数矩阵, 大小是1万×8, 1万就是词典里面的单词个数, 8是词向量的维度, 每个单词被映射为8维的词向量, 这个logistics regression分类器有161个参数, 输入是160维的向量, 所以分类器有160维的参数向量, 分类器还有一个bias偏置量, 所以一共有161个参数。