1.1 文本表示——词袋法:one-hot、TF-IDF、n-gram、神经概率语言模型

文章目录

一、文本表示

文本表示的意思是把字词处理成向量或矩阵,以便计算机能进行处理。文本表示是自然语言处理的开始环节。

文本表示分为离散表示和分布式表示。离散表示的代表就是词袋模型,one-hot(也叫独热编码)、TF-IDF、n-gram都可以看作是词袋模型。分布式表示也叫做词嵌入(word embedding),经典模型是word2vec,还包括后来的Glove、ELMO、GPT和最近很火的BERT。

这篇文章介绍一下文本的离散表示。

(一)one-hot

看一个简单的例子。有两句话“杨幂太可爱了,我爱杨幂”,“我要看杨幂的电视剧”,把这两句话拆成一个个的字,整理得到14个不重复的字,这14个字决定了在文本表示时向量的长度为14。

下面这个表格的第一行是第一句话构成的一个词袋(或者说词典),有8个字。要对第一句话进行数值表示,那么先构造一个11×8的零矩阵,然后找到第一句话中每个字在词典中出现的位置,把该位置的0替换为1,第二句话也这样处理。只管字出现了没有(出现了就填入1,不然就是0),而不管这个字在句子中出现了几次。

下面表格中就是“杨幂太可爱了,我爱杨幂”这话的one-hot表示。

右边是字典:
1 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 0
0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 1
0 0 0 0 0 0 1 0
0 0 0 0 1 0 0 0
1 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0

1、为什么需要one-hot编码?

one hot编码是将类别变量转换为机器学习算法易于利用的一种形式的过程。

上面的第一句话one hot相当于多分类的问题,每个样本只对应于一个类别(即只在对应的特征处值为1,其余地方值为0),而我们的分类结果,得到的往往是隶属于某个类别的概率,这样在进行损失函数(例如交叉熵损失)或准确率计算时,变得非常方便

2、one-hot编码的缺陷

第一个问题是数据稀疏和维度灾难。数据稀疏也就是向量的大部分元素为0,如果词袋中的字词达数百万个,那么由每篇文档转换成的向量的维度是数百万维,由于每篇文档去重后字数较少,因此向量中大部分的元素是0。而且对数百万维的向量进行计算是一件比较蛋疼的事。 但是这样进行文本表示有几个问题。可见,尽管两个句子的长度不一样,但是one-hot编码后长度都一样了,方便进行矩阵运算。

第二个问题是没有考虑句中字的顺序性,假定字之间相互独立。这意味着意思不同的句子可能得到一样的向量。比如“我太可爱了,杨幂爱我”,“杨幂要看我的演唱会”,得到的one-hot编码和上面两句话的是一样的。

第三个问题是没有考虑字的相对重要性。这种表示只管字出现没有,而不管出现的频率,但显然一个字出现的次数越多,一般而言越重要(除了一些没有实际意义的停用词)。

代码实现

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.preprocessing.text import Tokenizer

text1 = "杨幂太可爱了,我爱杨幂"
text2 = "我要看杨幂的电视剧"

text = [ text1,text2 ]

def seg_cut(s):
    a =[]
    for i in s:
        for j in i:
            a.append(j)
    return a


text = seg_cut(text)
text =    [' '.join(text)]
print(text)

tokenizer = Tokenizer(num_words=100)
tokenizer.fit_on_texts(text)
word_index = tokenizer.word_index
print(word_index)

text_index = tokenizer.texts_to_sequences(text)
print(text_index)


one_hot= tf.one_hot(text_index,depth = len(word_index)+1)#由于默认0也是一个类别,这里词典的维度加1
print(one_hot)

one_hot = one_hot.numpy()[ :,:,1:]
print(one_hot) # ['杨 幂 太 可 爱 了 , 我 爱 杨 幂 我 要 看 杨 幂 的 电 视 剧'] 的one_hot

['杨 幂 太 可 爱 了 , 我 爱 杨 幂 我 要 看 杨 幂 的 电 视 剧']
{'杨': 1, '幂': 2, '爱': 3, '我': 4, '太': 5, '可': 6, '了': 7, ',': 8, '要': 9, '看': 10, '的': 11, '电': 12, '视': 13, '剧': 14}
[[1, 2, 5, 6, 3, 7, 8, 4, 3, 1, 2, 4, 9, 10, 1, 2, 11, 12, 13, 14]]
tf.Tensor(
[[[0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
  [0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]]], shape=(1, 20, 15), dtype=float32)
[[[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
  [1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]]]

自定义实现任意label起始的onehot:

import numpy as np

def one_hot(labels,depth,begain = 1):
    """从1开始的one_hot"""
    one_hot_label = np.array([[int(i == int(labels[j])) for i in range( begain,depth+1)] for j in range(len(labels))])
    return one_hot_label


text_index = tf.reshape(text_index,shape=[-1]).numpy()
one_hot =one_hot(text_index,depth = len(word_index)) 
print(one_hot)
[[1 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 1 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 1 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 1 0 0 0 0 0 0 0 0]
 [0 0 1 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0]
 [0 0 0 1 0 0 0 0 0 0 0 0 0 0]
 [0 0 1 0 0 0 0 0 0 0 0 0 0 0]
 [1 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 1 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 1 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 1 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 1 0 0 0 0]
 [1 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 1 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 1 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 1 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 1 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 1]]

(二)词袋法(Bag of word,简称BOW)

1.1 文本表示——词袋法:one-hot、TF-IDF、n-gram、神经概率语言模型

词袋,是一种只统计词频的手段。

该模型忽略了文本的语法和语序,用一组无序的单词(words)来表示一段文字或一个文档(我们也可以认为一段文字也是一个文档,下文均用文档来表示),词袋法的核心是使用单词在文档中出现的**次数(频数)**来表示文档。

例如给出以下两个文档以及基于两个文档构建的一个词典dict:

  • s1: this is a sample is just a sample
  • s2: this is another example is another example
  • dict: this sample another example

上面的字典中包含4个单词,所以可以用4维的向量去表示每一个词典中的单词,

this:[1,0,0,0] ,sample:[0,1,0,0] ,another:[0,0,1,0], example:[0,0,0,1]

那么两个文档用词袋法分别可以表示为

s1:[1,2,0,0], s2:[1,0,2,2]

由此可以看出,词袋法其实相当于文档内各个单词(必须出现在给定的词典中哦)哑编码的累加。

词集法(Set of word,简称SOW):

是词袋法的的一个变种,和词袋法原理一样,也是以文档中的单词来表示文档的一种模型,区别在于:词袋法使用的是单词的频数,而在词集法中使用的是单词是否出现,出现赋值为1,否则赋值为0 同样对于上例来说,用词集法表示两个文档的话,分别为:

s1:[1,1,0,0] s2:[1,0,1,1]

(三)词频–逆文档频率(TF-IDF):

在词袋法或词集法中,使用的是单词的词频或者是否存在来进行文档的表征,但是不同的单词在不同的文档中出现的次数不同,而且有些单词仅仅在某一些文档中出现(比如专业名词等),也就是说不同单词对于文本而言具有不同的重要性,那么,如何评估一个单词对于一个文本的重要性呢?在讨论之前,我们有如下的假设:

  1. 单词的重要性随着它在文本中出现的次数成正比,也就是单词出现的次数越多,该单词对于文档越重要。

  2. 与此同时,单词的重要性会随着在语料库中出现的频率成反比下降,也就是单词在语料库中出现的频率越高,表示该单词越常见,也就是该单词对于特定文本的重要性越低。

TF-IDF用来评估字词对于文档集合中某一篇文档的重要程度。字词的重要性与它在某篇文档中出现的次数成正比,与它在所有文档中出现的次数成反比。TF-IDF的计算公式为:

1.1 文本表示——词袋法:one-hot、TF-IDF、n-gram、神经概率语言模型

下面一步步来理解。

1 TF——词频

TF(term frequency),即词频,用来衡量字在一篇文档中的重要性,计算公式为:

1.1 文本表示——词袋法:one-hot、TF-IDF、n-gram、神经概率语言模型

2 IDF——逆文档频率

IDF((inverse document frequency),叫做逆文档频率,衡量某个字在所有文档集合中的常见程度。当包含某个字的文档的篇数越多时,这个字也就烂大街了,重要性越低。计算公式为:

1.1 文本表示——词袋法:one-hot、TF-IDF、n-gram、神经概率语言模型

假设单词用t表示,文档用 d d d表示,语料库用 D D D表示,那么 N ( t , D ) N(t,D) N(t,D)表示包含单词 t t t的文档数量, ∣ D ∣ |D| ∣D∣表示文档数量, ∣ d ∣ |d| ∣d∣表示文档d中所有单词数量。 N ( t , d ) N(t,d) N(t,d)表示在文档 d d d中单词 t t t出现的次数。则有:
T F − I D F ( t , d , D ) = T F ( t , d ) ∗ I D F ( t , D ) = N ( t , d ) ∣ d ∣ × log ⁡ ( ∣ D ∣ + 0.00001 N ( t , D ) + 0.00001 ) TF-IDF(t,d,D) = TF(t,d)*IDF(t,D) = \frac{N(t,d)}{|d|} \times \log(\frac{|D|+0.00001}{N(t,D)+0.00001} ) TF−IDF(t,d,D)=TF(t,d)∗IDF(t,D)=∣d∣N(t,d)​×log(N(t,D)+0.00001∣D∣+0.00001​)
(此处分子分母分别加0.00001是为了防止出现为零的情况)

TF(t,d)和IDF(t,D) 除了使用默认的公式外,还可以使用一些扩展的公式

1.1 文本表示——词袋法:one-hot、TF-IDF、n-gram、神经概率语言模型

1.1 文本表示——词袋法:one-hot、TF-IDF、n-gram、神经概率语言模型

python 实现TF-IDF算法

collections模块 :namedtuple、deque、defaultdict、OrderedDict、ChainMap、Counter

from collections import defaultdict ,Counter
import math
import operator
import string 

stopwords_path = 

#设置三段文本
text_1 = "In information retrieval, tf–idf or TFIDF, short for term frequency–inverse document frequency, is a numerical statistic that is intended to reflect how important a word is to a document in a collection or corpus. It is often used as a weighting factor in searches of information retrieval, text mining, and user modeling. The tf–idf value increases proportionally to the number of times a word appears in the document and is offset by the number of documents in the corpus that contain the word, which helps to adjust for the fact that some words appear more frequently in general. Tf–idf is one of the most popular term-weighting schemes today; 83% of text-based recommender systems in digital libraries use tf–idf."
text_2 = "Variations of the tf–idf weighting scheme are often used by search engines as a central tool in scoring and ranking a document's relevance given a user query. tf–idf can be successfully used for stop-words filtering in various subject fields, including text summarization and classification."
text_3 = "One of the simplest ranking functions is computed by summing the tf–idf for each query term; many more sophisticated ranking functions are variants of this simple model."


punctuation_map = dict((ord(char), None) for char in string.punctuation)  
#引入标点符号,为下步去除标点做准备

def stop_words(stopwords_path=stopwords_path):
     # 加载停用词列表
    with open(stopwords_path, 'r', encoding='utf-8') as swf:
        return [word.strip() for word in swf.readlines()]

def create_vocab(text):
    l_text = text.lower()     #全部转化为小写以方便处理 
    without_punctuation = l_text.translate(punctuation_map)    #去除文章标点符号
    tokens = without_punctuation.strip().split()
    without_stopwords = [w for w in tokens if not w in stop_words()]    #去除文章的停用词
    count = Counter(without_stopwords)                 #实现计数功能
    return count



def TF_IdF(token, token_words ,  documents_word):
    
    def D_con(token,  documents_word ): 
        # 语料库中的文档总数
        D_con = 0
        for counts in documents_word:
            if token in counts:
                D_con += 1
        return D_con

    tf = token_words[token] / sum(token_words.values())

    idf =  math.log( (len(documents_word)+0.00001)  / (0.00001 + D_con(token, documents_word)))

    return tf * idf



def word_select(documents):
    count_list = [] 
    for text in documents: 
        count_list.append(create_vocab(text))      #填入清洗好后的文本
    tf_idfs = {}
    for i in range(len(count_list)): #每篇
        tf_idf = {}
        for word in count_list[i]:  # 每篇的word_count
            tf_idf[word] = TF_IdF(word, count_list[i], count_list)
        sort = sorted(tf_idf.items(), key = lambda x: x[1], reverse=True) #将集合按照TF-IDF值从大到小排列
        tf_idfs[i] = sort

    return tf_idfs


if __name__=='__main__':

    texts = [text_1, text_2, text_3] 
    q = word_select(texts)
    for i ,word_count in q.items() :
        print('For document {}'.format(i+1))
        for word, tf_idf in word_count[:7]: 
            print("\tWord: {} : {}".format(word, round(tf_idf, 6)))
For document 1
	Word: document : 0.052315
	Word: word : 0.052315
	Word: a : 0.038616
	Word: information : 0.034876
	Word: retrieval : 0.034876
	Word: corpus : 0.034876
	Word: number : 0.034876
For document 2
	Word: a : 0.045051
	Word: variations : 0.040689
	Word: scheme : 0.040689
	Word: search : 0.040689
	Word: engines : 0.040689
	Word: central : 0.040689
	Word: tool : 0.040689
For document 3
	Word: functions : 0.156944
	Word: simplest : 0.078472
	Word: computed : 0.078472
	Word: summing : 0.078472
	Word: sophisticated : 0.078472
	Word: variants : 0.078472
	Word: simple : 0.078472

Jieba实现TF-IDF算法

意境级讲解 jieba分词和词云、关键词抽取:TextRank、TF-IDF

import jieba.analyse
 
text='关键词是能够表达文档中心内容的词语,常用于计算机系统标引论文内容特征、
信息检索、系统汇集以供读者检阅。关键词提取是文本挖掘领域的一个分支,是文本检索、
文档比较、摘要生成、文档分类和聚类等文本挖掘研究的基础性工作'
 
keywords=jieba.analyse.extract_tags(text, topK=5, withWeight=False, allowPOS=())
print(keywords)
['文档', '文本', '关键词', '挖掘', '文本检索']

注:

  • jieba.analyse.extract_tags(sentence, topK=20, withWeight=False, allowPOS=())
  • sentence 为待提取的文本
  • topK 为返回几个 TF/IDF 权重最大的关键词,默认值为 20
  • withWeight 为是否一并返回关键词权重值,默认值为 False
  • allowPOS 仅包括指定词性的词,默认值为空,即不筛选

(三)n-gram

上面词袋模型假设字与字之间是相互独立的,没有考虑它们之间的顺序。于是引入语言模型。

语言模型

一个语言模型通常表现为构建字符串 T T T的概率分布 P ( T ) P(T) P(T),这里 P ( T ) P(T) P(T)试图反映的是字符串 T T T作为一个句子出现的频率,即是组成字符串的这个组合在训练语料库中出现的似然。需要注意的是,与语言学中不同,语言模型与句子是否合乎语法逻辑无关,即使一个句子组合完全合乎语法,但这个组合在语料库中出现的似然极小,我们仍然可以认为它出现的概率接近为零。

  • 对于一个由 k k k个基元(“基元”可以为字、词或短语等)构成的句子 T = W 1 W 2 … W k T= W_{1} W_{2} \ldots W_{\mathrm{k}} T=W1​W2​…Wk​,其概率计算公式可以表示为:

P ( T ) = P ( W 1 , W 2 , W 3 , … W k ) = P ( W 1 ) P ( W 2 ∣ W 1 ) P ( W 3 ∣ W 1 W 2 ) … P ( W k ∣ W 1 W 2 … W k − 1 ) P(T)=P\left(W_{1}, W_{2}, W_{3}, \ldots W_{k}\right)=P\left(W_{1}\right) P\left(W_{2} \mid W_{1}\right) P\left(W_{3} \mid W_{1} W_{2}\right) \ldots P\left(W_{\mathrm{k}} \mid W_{1} W_{2} \ldots W_{\mathrm{k}-1}\right) P(T)=P(W1​,W2​,W3​,…Wk​)=P(W1​)P(W2​∣W1​)P(W3​∣W1​W2​)…P(Wk​∣W1​W2​…Wk−1​)

语言模型背后的假设是: 一个词出现的概率跟他前面的所有词都相关。然而这样的假设导致计算量非常大。

于是就有了 n \mathrm{n} n -gram 语言模型,即假定一个词出现的概率只与它前面固定数目的词有关,这就是 n \mathrm{n} n -gram 的基本思想。n-gram 做了一个 n − 1 n-1 n−1 阶的 Markov 假设,认为一个词出现的概率只与它前面的 n − 1 n-1 n−1 个词相关, 而这 n n n 个词就构成一个gram.

一般2-gram或者3-gram比较常见。

计算

n-gram 假设某个词仅跟它前面几个有限的词相关,因此也就不必追溯到最开始的那个词,这样便 可以大幅缩咸概率公式的长度。即
p ( w k ∣ w 1 , … , w k − 1 ) ≈ p ( w k ∣ w k − n + 1 , … , w k − 1 ) ≈ count ⁡ ( w k − n + 1 , … , w k − 1 , w k ) count ⁡ ( w k − n + 1 , … , w k − 1 ) p\left(w_{k} \mid w_{1}, \ldots, w_{k-1}\right) \approx p\left(w_{k} \mid w_{k-n+1}, \ldots, w_{k-1}\right) \approx \frac{\operatorname{count}\left(w_{k-n+1}, \ldots, w_{k-1}, w_{k}\right)}{\operatorname{count}\left(w_{k-n+1}, \ldots, w_{k-1}\right)} p(wk​∣w1​,…,wk−1​)≈p(wk​∣wk−n+1​,…,wk−1​)≈count(wk−n+1​,…,wk−1​)count(wk−n+1​,…,wk−1​,wk​)​
比如让 n = 2 n=2 n=2, 则 p ( w k ∣ w k − 1 ) = count ⁡ ( w k − 1 , w k ) count ⁡ ( w k − 1 ) \large p\left(w_{k} \mid w_{k-1}\right)=\frac{\operatorname{count}\left(w_{k-1}, w_{k}\right)}{\operatorname{count}\left(w_{k-1}\right)} p(wk​∣wk−1​)=count(wk−1​)count(wk−1​,wk​)​, 这一简化让单个参数的统计变得更容易, 因为统计时需要匹配的词串更短。

比如“邓紫棋太可爱了,我爱邓紫棋”,“我要看邓紫棋的演唱会”这两个句子,分解为2-gram词汇表:

{邓,邓紫,紫,紫棋,棋,棋太,太,太可,可,可爱,爱,爱了,了,了我,我,我爱,爱邓,我要,要,要看,看邓,棋的,的,的演,演,演唱,唱会,会}

于是原来只有14个字的1-gram字典(就是一个字一个字进行划分的方法)就成了28个元素的2-gram词汇表,词表的维度增加了一倍。

N-gram模型认为第N个词的出现只与前面N-1个词相关,而与其它词都不相关。整句的概率就是各个词出现概率的乘积。

特殊的n-gram模型

> > > Unigram (1-gram)
P ( T ) = P ( W 1 , W 2 , W 3 , … W T ) = P ( W 1 ) P ( W 2 ∣ W 1 ) P ( W 3 ∣ W 1 W 2 ) … P ( W T ∣ W 1 W 2 … W T − 1 ) ≈ P ( W 1 ) P ( W 2 ) P ( W 3 ) … P ( W T ) P(T)=P\left(W_{1}, W_{2}, W_{3}, \ldots W_{T}\right)=P\left(W_{1}\right) P\left(W_{2} \mid W_{1}\right) P\left(W_{3} \mid W_{1} W_{2}\right) \ldots P\left(W_{\mathrm{T}} \mid W_{1} W_{2} \ldots W_{\mathrm{T}-1}\right)\\ \approx P\left(W_{1}\right) P\left(W_{2}\right) P\left(W_{3}\right) \ldots P\left(W_{T}\right) P(T)=P(W1​,W2​,W3​,…WT​)=P(W1​)P(W2​∣W1​)P(W3​∣W1​W2​)…P(WT​∣W1​W2​…WT−1​)≈P(W1​)P(W2​)P(W3​)…P(WT​)
> B i g r a m ( 2 − g r a m ) >\mathrm{Bigram}(2-\mathrm{gram}) >Bigram(2−gram)
P ( T ) = P ( W 1 , W 2 , W 3 , … W T ) = P ( W 1 ) P ( W 2 ∣ W 1 ) P ( W 3 ∣ W 1 W 2 ) … P ( W T ∣ W 1 W 2 … W T − 1 ) ≈ P ( W 1 ) P ( W 2 ∣ W 1 ) P ( W 3 ∣ W 2 ) … P ( W T ∣ W T − 1 ) P(T)=P\left(W_{1}, W_{2}, W_{3}, \ldots W_{T}\right)=P\left(W_{1}\right) P\left(W_{2} \mid W_{1}\right) P\left(W_{3} \mid W_{1} W_{2}\right) \ldots P\left(W_{\mathrm{T}} \mid W_{1} W_{2} \ldots W_{\mathrm{T}-1}\right)\\ \approx P\left(W_{1}\right) P\left(W_{2} \mid W_{1}\right) P\left(W_{3} \mid W_{2}\right) \ldots P\left(W_{\mathrm{T}} \mid W_{\mathrm{T}-1}\right) P(T)=P(W1​,W2​,W3​,…WT​)=P(W1​)P(W2​∣W1​)P(W3​∣W1​W2​)…P(WT​∣W1​W2​…WT−1​)≈P(W1​)P(W2​∣W1​)P(W3​∣W2​)…P(WT​∣WT−1​)

> > > Trigram (3-gram)
P ( T ) = P ( W 1 , W 2 , W 3 , … W T ) = P ( W 1 ) P ( W 2 ∣ W 1 ) P ( W 3 ∣ W 1 W 2 ) … P ( W T ∣ W 1 W 2 … W T − 1 ) ≈ P ( W 1 ) P ( W 2 ∣ W 1 ) P ( W 3 ∣ W 1 W 2 ) … P ( W T ∣ W T − 2 W T − 1 ) P(T)=P\left(W_{1}, W_{2}, W_{3}, \ldots W_{\mathrm{T}}\right)=P\left(W_{1}\right) P\left(W_{2} \mid W_{1}\right) P\left(W_{3} \mid W_{1} W_{2}\right) \ldots P\left(W_{\mathrm{T}} \mid W_{1} W_{2} \ldots W_{\mathrm{T}-1}\right)\\ \approx P\left(W_{1}\right) P\left(W_{2} \mid W_{1}\right) P\left(W_{3} \mid W_{1} W_{2}\right) \ldots P\left(W_{\mathrm{T}} \mid W_{\mathrm{T}-2} W_{\mathrm{T}-1}\right) P(T)=P(W1​,W2​,W3​,…WT​)=P(W1​)P(W2​∣W1​)P(W3​∣W1​W2​)…P(WT​∣W1​W2​…WT−1​)≈P(W1​)P(W2​∣W1​)P(W3​∣W1​W2​)…P(WT​∣WT−2​WT−1​)

算法实例

1.2 Bigram计算句子的概率、python实现

即语料库: 研究生物很有意思。他大学时代是研究生物的。生物专业是他的首选目标。他是研究生。

测试句子:研究生物专业是他的首选目标

使用二元模型以分词模式计算出测试句子的概率。

平滑化

这里有一个要注意的问题: 平滑化, 即当 count ⁡ ( w k − n + 1 , … , w k − 1 , w k ) = 0 \operatorname{count}\left(w_{k-n+1}, \ldots, w_{k-1}, w_{k}\right)=0 count(wk−n+1​,…,wk−1​,wk​)=0, p ( w k ∣ w k − n + 1 , … , w k − 1 ) = 0 p\left(w_{k} \mid w_{k-n+1}, \ldots, w_{k-1}\right)=0 p(wk​∣wk−n+1​,…,wk−1​)=0, 能否认为 p ( w k ∣ w 1 , … , w k − 1 ) = 0 p\left(w_{k} \mid w_{1}, \ldots, w_{k-1}\right)=0 p(wk​∣w1​,…,wk−1​)=0 呢?显然不
能, 因此需要平滑技术来处理这个问题。

1.3 n-gram平滑算法:Good-Turning、拉普拉斯平滑

(四)神经概率语言模型

由 n \mathrm{n} n -gram 模型可以看到,计算一个句子的概率, 关键步骤在于计算条件概率 p ( w k ∣ w k − n + 1 , … , w k − 1 ) p\left(w_{k} \mid w_{k-n+1}, \ldots, w_{k-1}\right) p(wk​∣wk−n+1​,…,wk−1​), 即给定单词 w k w_{k} wk​ 的上下文 context ⁡ ( w k ) \operatorname{context}\left(w_{k}\right) context(wk​), 求单词 w k w_{k} wk​ 出现的概率 p ( w k ∣ context ⁡ ( w k ) ) p\left(w_{k} \mid \operatorname{context}\left(w_{k}\right)\right) p(wk​∣context(wk​)) 。在 n \mathrm{n} n -gram 中, context ⁡ ( w k ) = { w k − n + 1 , … , w k − 1 } \operatorname{context}\left(w_{k}\right)=\left\{w_{k-n+1}, \ldots, w_{k-1}\right\} context(wk​)={wk−n+1​,…,wk−1​}。

为了求出 p ( w k ∣ context ⁡ ( w k ) ) p\left(w_{k} \mid \operatorname{context}\left(w_{k}\right)\right) p(wk​∣context(wk​)), 除了用频率近似概率的思路外, 我们还可以将这个条件概 率看成一个函数, 即
p ( w k ∣ context ⁡ ( w k ) ) = F ( w k , context ⁡ ( w k ) , θ ) p\left(w_{k} \mid \operatorname{context}\left(w_{k}\right)\right)=F\left(w_{k}, \operatorname{context}\left(w_{k}\right), \theta\right) p(wk​∣context(wk​))=F(wk​,context(wk​),θ)
当确定参数 θ \theta θ后 F F F 也就唯一确定了,给定任意一对 ( context ⁡ ( w k ) , w k ) \left(\operatorname{context}\left(w_{k}\right), w_{k}\right) (context(wk​),wk​), 即可求出 p ( w k ∣ context ⁡ ( w k ) ) p\left(w_{k} \mid \operatorname{context}\left(w_{k}\right)\right) p(wk​∣context(wk​)) 。

与 n − g r a m \mathrm{n}-\mathrm{gram} n−gram 相比, 这种方法不需要保存所有概率值, 而是通过直接计算来获取概率值, 且通过选取合适的模型可让参数 θ \theta θ 的个数远小于 n \mathrm{n} n -gram 模型的参数个数。

这种方法最关键的地方在于函数 F F F 的构造。用神经网络去构造 F F F ,称为神经概率语言模型。

词向量

神经概率语言模型要用到一个工具,词向量。首先为词典 D \mathcal{D} D 的任意词 w w w 指定一个长度为 m m m 的 实值向量 v ( w ) ∈ R m , v ( w ) \mathbf{v}(w) \in \mathbb{R}^{m}, \mathbf{v}(w) v(w)∈Rm,v(w) 就是 w w w 的词向量。词向量的初始值是随机产生的,在训练过程中不断调整。

相对于 n-gram,神经概率语言模型不需要进行词串统计和存储,只要给定输入,可以直接计算概率输出。另外,该模型有一个副产品:词向量。词向量蕴含丰富的上下文信息,能够表示词之间的相似性,能服务于许多其他任务。

模型

神经概率语言模型通过神经网络来计算条件概率

p ( w k ∣ context ⁡ ( w k ) ) = F ( i w k , v ( context ⁡ ( w k ) ) , θ ) p\left(w_{k} \mid \operatorname{context}\left(w_{k}\right)\right)=F\left(i_{w_{k}}, \mathrm{v}\left(\operatorname{context}\left(w_{k}\right)\right), \theta\right) p(wk​∣context(wk​))=F(iwk​​,v(context(wk​)),θ)
其中, F F F 是神经网络, θ \theta θ 是网络待优化的参数, i w k i_{w_{k}} iwk​​ 是单词 w k w_{k} wk​ 在字典 D \mathcal{D} D 中的编号, v ( context ⁡ ( w k ) ) \mathrm{v}\left(\operatorname{context}\left(w_{k}\right)\right) v(context(wk​)) 是 w k w_{k} wk​ 的上下文 context ⁡ ( w k ) \operatorname{context}\left(w_{k}\right) context(wk​) 的词向量表示。模型的各层如下所述:

输入层: 将上下文 context ⁡ ( w k ) \operatorname{context}\left(w_{k}\right) context(wk​) 中每个词映射为一个长度为 m m m 的词向量, 词向量在训练时是随机初始化的, 并且参与训练。

投影层: 将所有上下文的词向量拼接为一个长向量, 作为隐藏层的输入。假设 context ⁡ ( w k ) = { w k − n + 1 , … , w k − 1 } \operatorname{context}\left(w_{k}\right)=\left\{w_{k-n+1}, \ldots, w_{k-1}\right\} context(wk​)={wk−n+1​,…,wk−1​}, 则
v ( context ⁡ ( w k ) ) = [ v ( w k − n + 1 ) , … , v ( w k − 1 ) ] \mathrm{v}\left(\operatorname{context}\left(w_{k}\right)\right)=\left[\mathrm{v}\left(w_{k-n+1}\right), \ldots, \mathrm{v}\left(w_{k-1}\right)\right] v(context(wk​))=[v(wk−n+1​),…,v(wk−1​)], 该向量的维度为 m ∗ ( n − 1 ) 。 m* (n-1) 。 m∗(n−1)。 当上下文的单词数小于 n − 1 n-1 n−1, 可人为补足向量。

隐藏层: v ( context ⁡ ( w k ) ) \mathrm{v}\left(\operatorname{context}\left(w_{k}\right)\right) v(context(wk​)) 经过一个规模为 h h h 的隐藏层, 该隐藏层使用 tanh ⁡ \tanh tanh 激活函数。用 户可自行指定 h h h 。

输出层: 隐藏层的输出经过一个规模为 N N N 的输出层, 得到 N N N 维输出。

Softmax层:输出层的输出不能表示概率,要通过 Softmax 进行归一化, 从而得到词表中每个词 作为下一个词的概率分布。

所有的计算步骤简化如下:

所有的计算步骤简化如下:
Z = tanh ⁡ ( W X + b 1 ) Y = U Z + b 2 p ( w ) = e Y i w ∑ j = 1 N e Y j \mathrm{Z}=\tanh (W \mathrm{X}+\mathrm{b}_1)\\ \mathrm{Y}=U \mathrm{Z}+\mathrm{b}_2\\ p(w)=\frac{e^{Y_{i_{w}}}}{\sum_{j=1}^{N} e^{Y_{j}}} Z=tanh(WX+b1​)Y=UZ+b2​p(w)=∑j=1N​eYj​eYiw​​​
其中, X = v ( context ⁡ ( w ) ) , p ( w ) X=\mathrm{v}(\operatorname{context}(w)), p(w) X=v(context(w)),p(w) 是下一个词为单词 w w w 的预测概率, i w i_{w} iw​ 是单词 w w w 在 字典 D \mathcal{D} D 中的编号, N N N 是字典的大小。 b 1 , b 2 \mathrm{b}_1, \mathrm{b}_2 b1​,b2​ 是偏置项。

整个模型可简写为: P = Softmax ⁡ ( U tanh ⁡ ( W X + b 1 ) + b 2 ) P=\operatorname{Softmax}(U \tanh (W \mathrm{X}+\mathrm{b}_1)+\mathrm{b}_2) P=Softmax(Utanh(WX+b1​)+b2​)

训练

每个训练样本基于 ( w k , context ⁡ ( w k ) ) \left(w_{k}, \operatorname{context}\left(w_{k}\right)\right) (wk​,context(wk​)) 构造, 模型的输入是 v ( context ⁡ ( w k ) ) \mathrm{v}\left(\operatorname{context}\left(w_{k}\right)\right) v(context(wk​)), 标签是一个 N N N 维的 one-hot 向量, 索引 i w k i_{w_{k}} iwk​​ 处是1, 其余是0, 表征单词 w k w_{k} wk​ 。

Softmax 层输出的概率分布和标签一起输入损失函数中计算损失,通过随机梯度下降反向更新网络参数。损失函数一般选取交叉滴。同一个网络只能训练特定的 n n n, 要实现不同的 n n n 需要训练不同的神经网络。

模型的超参数为词典 D \mathcal{D} D 的大小 N N N, 向量长度 m m m, 词和它上下文词个数 n n n, 藏层的规模 h h h 。 在实战中,它们的取值经验如下:

(1) n n n 是一个词的上下文中包含的词数通常不超过5;

(2) m m m 是词向量长度,通常是 [ 1 0 1 , 1 0 2 ] \left[10^{1}, 10^{2}\right] [101,102] 量级;

(3) h h h 由用户指定,通常不需取得太大如 1 0 2 10^{2} 102 量级;

(4) N N N 是语料词汇量的大小, 与语料相关,但通常是 [ 1 0 4 , 1 0 5 ] \left[10^{4}, 10^{5}\right] [104,105] 量级

参考

n-gram https://zhuanlan.zhihu.com/p/32829048

N-gram模型和神经语言模型 https://www.jianshu.com/p/20961a714326

word2vec 中的数学原理详解 https://www.cnblogs.com/peghoty/p/3

上一篇:中国半导体存储行业未来50年发展路线图


下一篇:curl_multi异步高并发服务实现