本文将介绍部分nlp相关概念
1分词
无论对于中文还是英文的文本处理任务,首先要做的都是分词操作。中文常用的是jieba分词,英文则直接按照空格进行分词操作。
1.1、正向最大匹配法:
正向即从前往后取词,从7->1,每次减一个字,直到词典命中或剩下1个单字。
第1次:“我们在野生动物”,扫描7字词典,无
第2次:“我们在野生动”,扫描6字词典,无
。。。。
第6次:“我们”,扫描2字词典,有
扫描中止,输出第1个词为“我们”,去除第1个词后开始第2轮扫描,即:
第2轮扫描:
第1次:“在野生动物园玩”,扫描7字词典,无
第2次:“在野生动物园”,扫描6字词典,无
。。。。
第6次:“在野”,扫描2字词典,有
扫描中止,输出第2个词为“在野”,去除第2个词后开始第3轮扫描,即:
第3轮扫描:
第1次:“生动物园玩”,扫描5字词典,无
第2次:“生动物园”,扫描4字词典,无
第3次:“生动物”,扫描3字词典,无
第4次:“生动”,扫描2字词典,有
扫描中止,输出第3个词为“生动”,第4轮扫描,即:
第4轮扫描:
第1次:“物园玩”,扫描3字词典,无
第2次:“物园”,扫描2字词典,无
第3次:“物”,扫描1字词典,无
扫描中止,输出第4个词为“物”,非字典词数加1,开始第5轮扫描,即:
第5轮扫描:
第1次:“园玩”,扫描2字词典,无
第2次:“园”,扫描1字词典,有
扫描中止,输出第5个词为“园”,单字字典词数加1,开始第6轮扫描,即:
第6轮扫描:
第1次:“玩”,扫描1字字典词,有
扫描中止,输出第6个词为“玩”,单字字典词数加1,整体扫描结束。
正向最大匹配法,最终切分结果为:“我们/在野/生动/物/园/玩”,其中,单字字典词为2,非词典词为1。
1.2、逆向最大匹配法:
逆向即从后往前取词,其他逻辑和正向相同。即:
第1轮扫描:“在野生动物园玩”
第1次:“在野生动物园玩”,扫描7字词典,无
第2次:“野生动物园玩”,扫描6字词典,无
。。。。
第7次:“玩”,扫描1字词典,有
扫描中止,输出“玩”,单字字典词加1,开始第2轮扫描
第2轮扫描:“们在野生动物园”
第1次:“们在野生动物园”,扫描7字词典,无
第2次:“在野生动物园”,扫描6字词典,无
第3次:“野生动物园”,扫描5字词典,有
扫描中止,输出“野生动物园”,开始第3轮扫描
第3轮扫描:“我们在”
第1次:“我们在”,扫描3字词典,无
第2次:“们在”,扫描2字词典,无
第3次:“在”,扫描1字词典,有
扫描中止,输出“在”,单字字典词加1,开始第4轮扫描
第4轮扫描:“我们”
第1次:“我们”,扫描2字词典,有
扫描中止,输出“我们”,整体扫描结束。
逆向最大匹配法,最终切分结果为:“我们/在/野生动物园/玩”,其中,单字字典词为2,非词典词为0。
1.3、双向最大匹配法:
正向最大匹配法和逆向最大匹配法,都有其局限性,我举得例子是正向最大匹配法局限性的例子,逆向也同样存在(如:长春药店,逆向切分为“长/春药店”),因此有人又提出了双向最大匹配法,双向最大匹配法。即,两种算法都切一遍,然后根据大颗粒度词越多越好,非词典词和单字词越少越好的原则,选取其中一种分词结果输出。
如:“我们在野生动物园玩”
正向最大匹配法,最终切分结果为:“我们/在野/生动/物/园/玩”,其中,两字词3个,单字字典词为2,非词典词为1。
逆向最大匹配法,最终切分结果为:“我们/在/野生动物园/玩”,其中,五字词1个,两字词1个,单字字典词为2,非词典词为0。
非字典词:正向(1)>逆向(0)(越少越好)
单字字典词:正向(2)=逆向(2)(越少越好)
总词数:正向(6)>逆向(4)(越少越好)
因此最终输出为逆向结果。
2、语言模型概念
unigram 一元分词,把句子分成一个一个的汉字
bigram 二元分词,把句子从头到尾每两个字组成一个词语
trigram 三元分词,把句子从头到尾每三个字组成一个词语.
2.1 unigram、bigram频率统计
基于python代码
class NGram(object):
def __init__(self, n):
# n is the order of n-gram language model
self.n = n
self.unigram = {}
self.bigram = {}
def scan(self, sentence):
# file your code here
for line in sentence:
self.ngram(line.split())
#unigram
if self.n == 1:
try:
fip = open("data.uni","w")
except:
print >> sys.stderr ,"failed to open data.uni"
for i in self.unigram:
fip.write("%s %d\n" % (i,self.unigram[i]))
if self.n == 2:
try:
fip = open("data.bi","w")
except:
print >> sys.stderr ,"failed to open data.bi"
for i in self.bigram:
fip.write("%s %d\n" % (i,self.bigram[i]))
# caluclate the ngram of the words
#
# @param words list{str}
# @return none
def ngram(self, words):
# unigram
if self.n == 1:
for word in words:
if word not in self.unigram:
self.unigram[word] = 1
else:
self.unigram[word] = self.unigram[word] + 1
# bigram
if self.n == 2:
num = 0
stri = ''
for i in words:
num = num + 1
if num == 2:
stri = stri + " "
stri = stri + i
if num == 2:
if stri not in self.bigram:
self.bigram[stri] = 1
else:
self.bigram[stri] = self.bigram[stri] + 1
num = 0
stri = ''
if __name__=="__main__":
import sys
try:
fip = open(sys.argv[1],"r")
except:
print >> sys.stderr, "failed to open input file"
sentence = []
for line in fip:
if len(line.strip())!=0:
sentence.append(line.strip())
uni = NGram(1)
bi = NGram(2)
uni.scan(sentence)
bi.scan(sentence)
3 文本矩阵化
如果是句子,需要进行分词
如果是文件,需要将文件处理为每一行对应一个句子(已经分词,以空格隔开),实例处理过程见文末。
法一:语料处理为列表
from gensim.models import Word2Vec
sentences = [["Python", "深度学习", "机器学习"], ["NLP", "深度学习", "机器学习"]]
model = Word2Vec(sentences, min_count=1)
把Python内置列表当作输入很方便,但当输入量很大的时候,大会占用大量内存。
法二:语料是文件(处理为迭代器)
Gensim需要输入一个可迭代的列表,可以是迭代器,没有必要把一切东西都保存在内存中,提供一个语句,加载处理它,忘记它,加载另一个语句。
一般我们的语料是在文件中存放的,首先,需要保证语料文件内部每一行对应一个句子(已经分词,以空格隔开),方法见上。
对一个目录下的所有文件生效(法1)
这些文件已经被分词好了,如果还需要进一步预处理文件中的单词,如移除数字,提取命名实体… 所有的这些都可以在MySentences 迭代器内进行,保证给work2vec的是处理好的迭代器。
class MySentences(object):
def __init__(self, dirname):
self.dirname = dirname
def __iter__(self):
for fname in os.listdir(self.dirname):
for line in open(os.path.join(self.dirname, fname)):
yield line.split()
sentences = MySentences('/some/directory') # a memory-friendly iterator
model = gensim.models.Word2Vec(sentences)
对于单个文件语料,使用LineSentence
class: gensim.models.word2vec.LineSentence
每一行对应一个句子(已经分词,以空格隔开),我们可以直接用LineSentence把txt文件转为所需要的格式。
LineSentence功能解释:Iterate over a file that contains sentences: one line = one sentence. Words must be already preprocessed and separated by whitespace(对包含句子的文件进行迭代:一行=一句话。单词必须经过预处理,并由空格分隔)
from gensim import Word2Vec
from gensim.Word2Vec import LineSentence
from gensim.test.utils import common_texts, get_tmpfile
# inp为输入语料
inp = 'wiki.zh.text.jian.seg.txt'
sentences = LineSentences(inp)
path = get_tmpfile("word2vec.model") #创建临时文件
model = Word2Vec(sentences, size=100, window=5, min_count=1)
model.save("word2vec.model")
训练
import jieba
import jieba.analyse
from gensim.test.utils import common_texts, get_tmpfile
from gensim.models import Word2Vec
#文件位置需要改为自己的存放路径
#将文本分词
with open('C:\\Users\Administrator\Desktop\\in_the_name_of_people\in_the_name_of_people.txt',encoding='utf-8') as f:
document = f.read()
document_cut = jieba.cut(document)
result = ' '.join(document_cut)
with open('./in_the_name_of_people_segment.txt', 'w',encoding="utf-8") as f2:
f2.write(result)
#加载语料
sentences = word2vec.LineSentence('./in_the_name_of_people_segment.txt')
#训练语料
path = get_tmpfile("word2vec.model") #创建临时文件
model = word2vec.Word2Vec(sentences, hs=1,min_count=1,window=10,size=100)
# model.save("word2vec.model")
# model = Word2Vec.load("word2vec.model")
#输入与“贪污”相近的100个词
for key in model.wv.similar_by_word('贪污', topn =100):
print(key)
#输出了100个,示例前几个
('地皮', 0.9542419910430908)
('高昂', 0.934522807598114)
('证', 0.9154356122016907)
('上强', 0.9113685488700867)
('一抹', 0.9097814559936523)
('得罪', 0.9082552790641785)
('屁股', 0.9072068929672241)
('能伸能屈', 0.9049990177154541)
('二十五万', 0.9045952558517456)