Datawhale《深度学习-NLP》Task2- 基本文本处理技能,语言模型,文本矩阵化

4. 参考 结巴分词介绍和使用:GitHub - fxsjy/jieba: 结巴中文分词

 

基本文本处理

1. 分词

分词的正向最大

如果叫成最长正向分词要更形象一些,是一种分治+贪婪的思想,并不一时处理全部串,而是分别处理预先设立长度的每一段,在每一段中求取最长的并且出现在字典里面的词。例如:abcdef,预先设立的最大长度为3。所以,先从串的开始截取长度为三的子串,即abc,如果abc出现在字典中,那么abc将作为分词结果,接着以相同方式处理def;如果abc没出现在字典里面,则从右边减少一个字符,再次匹配字典,即ab匹配,减少的字符将加入之前未选择的字符集里面作下一次匹配,这里是cdef,如果一个串没匹配到长度大于1的字典词,则返回最左边的字符作为该串的分词结果,也就是ab如果没有匹配到,无论a是否在字典中,都将作为分词结果。过程如图:

è¿éåå¾çæè¿°

正向伪代码
result = []
right_seq = input_seq
while True:
    select_seq,left_seq = get_select_seq(right_seq)
    seg_word,seg_word_right = selectMaxLenInDict(select_seq)
    result.append(seg_seq)
    right_seq = seg_word_right+left_seq
    if right_seq=="":
        break

def selectMaxLenInDict(_seq):
    for idx in range(len(select_seq),1,-1):
        word = select_seq[0:idx]
        if word in dict:
        return word,select_seq[idx:]
    return select_seq[0:1],select_seq[1:]

def get_seg_seq(_seq):
    if maxlen > len(_seq):
        return _seq,""
    return _seq[0:maxlen],_seq[maxlen:]

分词的逆向最大

有了正向最大分词,逆向就很好理解了,正向是从前向后选取最大长度的串,然后从选取串的尾部向前匹配字典词,删除右边的字符。逆向最大便是从后向前选取最大长度的串,从选取串开始向后匹配字典词,而删减的也便是左边的字符。

逆向伪代码
result = []
right_seq = input_seq
while True:
    left_seq,select_seq = get_select_seq(right_seq)
    seg_word,seg_word_right = selectMaxLenInDict(select_seq)
    result.append(seg_seq)
    right_seq =left_seq+ seg_word_right
    if right_seq=="":
        break

def selectMaxLenInDict(_seq):
    for idx in range(0,len(select_seq)):
        word = select_seq[idx:]
        if word in dict:
        return select_seq[0:idx],word
    return select_seq[0:-1],select_seq[-1]

def get_seg_seq(_seq):
    if maxlen > len(_seq):
        return _seq,""
    return _seq[0:maxlen],_seq[maxlen:]

分词的双向最大匹配法

双向就是结合正向最大和逆向最大的结果,对两个结果进行比较,从而决定正确的分词方式,当前向和逆向分词数量不相等的时候,选择数量较少的那个分词结果。如果分词数量相同,则依次比较分词结果,相应位置如果分词相同,则选取为最后的结果中,如果相应位置分词结果不一样则选取字符较少的那个最为分词结果
 

说明:

正向、逆向、双向最大分词是最基本的分词方法,但分词准确性并不很高,常常是作为分词的Baseline使用。这类方法的实现比较简单,其中,可以利用字典树(Tire Tree)来实现,可参见:

http://blog.csdn.net/yangyan19870319/article/details/6399871的实现方法。
 

 

2. 词、字符频率统计

collections.Counter模块

#引入collections 包
import re
from collections import Counter

# 第一种方法
def get_max_value_v1(text):
    # 统一为小写字母
    text = text.lower()

    # 返回所有的字母
    result = re.findall('[a-zA-Z]',text)  

    # 返回字典的频数
    count = Counter(result)

    # 获取最大频数
    max_value = max(count.values())

    # 获取频数最大的字母
    max_list = []
    for k,v in count.items():
        if v == max_value:
            max_list.append(k)

    # 返回排序后的字母列表
    return sorted(max_list)

 #第二种方法
def get_max_value_v2(text):

    # 带条件的生成器
    count = Counter([x for x in text.lower() if x.isalpha()])

    # 获取最大值
    m = max(count.values())

    # 带字典的生成器
    return sorted([x for (x,y) in count.items() if y == m])

  # 获取字母频率
    s = 'My Name is James'.lower()
    list(map(s.count,s))

参考:使用Python+NLTK实现英文单词词频统计

https://blog.csdn.net/lyb3b3b/article/details/75098778

 

语言模型

1.语言模型中unigram、bigram、trigram的概念

举例:

原句:西安交通大学:

unigram 形式为:西/安/交/通/大/学

bigram形式为: 西安/安交/交通/通大/大学

trigram形式为:西安交/安交通/交通大/通大学

 

2. unigram、bigram频率统计

#!/usr/bin/env python  
  
class NGram(object):  
  
    def __init__(self, n):  
        # n is the order of n-gram language model  
        self.n = n  
        self.unigram = {}  
        self.bigram = {}  
  
    # scan a sentence, extract the ngram and update their  
    # frequence.  
    #  
    # @param    sentence    list{str}  
    # @return   none  
    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)  

 

文本矩阵化

一般步骤:

  1. 对文本分词(作为特征),比如把这句话“我是天才”分词为“我”“是”“天才”(这里面的学问就更多了,本文不赘述)
  2. 统计各词在句子中是否出现(词集模型)
  3. 统计各词在句子中出现次数(词袋模型)
  4. 统计各词在这个文档的TFIDF值(词袋模型+IDF值)

三种文本特征向量化方法:

词集模型:one-hot编码向量化文本
词袋模型+IDF:TFIDF向量化文本
哈希向量化文本

 

从字面意义上来看,文档包含词、短语、句子和段落等要素,在多数文本分类方法中,都将文本中出现的这些要素作为文本特征,而且随着要素级别的增高,其表达的语义越清晰,附带的信息也越丰富,但是特征组合的数目也会越大,因此,很少使用句子和段落作为特征。根据研究人员的实验,目前常见的特征项表示方法有:词、短语(Phrase)和 N-gram 项等。

词袋模型将一段文本作为一个个分离的词进行处理,通过不同类文本中可能出现词的差异对文本进行分类。必须指出的是对于文本分类,上下文对于其真正的类别有着强相关性。此类方法可能只是为了算法的简易性选择性地放弃了文本中的上下文信息,或者说只关注词频信息也能够获得符合要求的文本分类效果。

词袋模型的三部曲:分词(tokenizing),统计修订词特征值(counting)与标准化(normalizing)。

在词袋模型统计词频的时候,可以使用 sklearn 中的 CountVectorizer 来完成。

from sklearn.feature_extraction.text import CountVectorizer
 
vectorizer = CountVectorizer(min_df=1)
 
corpus = [      'This is the first document.',
    		'This is the second second document.',
    		'And the third one.',
		'Is this the first document?',
		]
X = vectorizer.fit_transform(corpus)
feature_name = vectorizer.get_feature_names()
 
print('============== X ===================')
print(X)
print('============== feature name =======')
print(feature_name)
print('============== X.toarray ==========')
print(X.toarray())
'''
# Output
============== X ===================
  (0, 1)        1
  (0, 2)        1
  (0, 6)        1
  (0, 3)        1
  (0, 8)        1
  (1, 5)        2
  (1, 1)        1
  (1, 6)        1
  (1, 3)        1
  (1, 8)        1
  (2, 4)        1
  (2, 7)        1
  (2, 0)        1
  (2, 6)        1
  (3, 1)        1
  (3, 2)        1
  (3, 6)        1
  (3, 3)        1
  (3, 8)        1
============== feature name =======
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']
============== X.toarray ==========
[[0 1 1 1 0 0 1 0 1]
 [0 1 0 1 0 2 1 0 1]
 [1 0 0 0 1 0 1 1 0]
 [0 1 1 1 0 0 1 0 1]]
'''

CountVectorizer 的 fit_transform 方法返回一个词频向量化矩阵,对应矩阵的第 i 行第 j 列表示第 i 个文本包含多少个第 j 个特征词。

中文分词可以借助Jieba分词:

# encoding=utf-8
import jieba

seg_list = jieba.cut("我来到北京清华大学", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list))  # 全模式

seg_list = jieba.cut("我来到北京清华大学", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list))  # 精确模式

seg_list = jieba.cut("他来到了网易杭研大厦")  # 默认是精确模式
print(", ".join(seg_list))

seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造")  # 搜索引擎模式
print(", ".join(seg_list))

输出:

【全模式】: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学

【精确模式】: 我/ 来到/ 北京/ 清华大学

【新词识别】:他, 来到, 了, 网易, 杭研, 大厦    (此处,“杭研”并没有在词典中,但是也被Viterbi算法识别出来了)

【搜索引擎模式】: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造

参考:https://github.com/fxsjy/jieba

 

TF-IDF处理

但是对于某些词,比如上述中的 is, the, 它们在文本中出现的频次很高,但是由于每类文本都具有大量的此类特征词,因此此类特征词对于文本分类的作用是很小的。如果一味追求词频可能将造成最后算法效果的下降。为评估或者说去除此类问题的发生,通常引入 TF-IDF 来对词频进行调整。

词频(Term Frequency,TF):

逆向文件频率(Inverse Document Frequency,IDF):

某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。

可以在使用上述方法之后,再对结果进行调整。

 
from sklearn.feature_extraction.text import TfidfTransformer 
from sklearn.feature_extraction.text import CountVectorizer 
 
 
corpus = [          'This is the first document.',
		'This is the second second document.',
		'And the third one.',
		'Is this the first document?',
		]
 
 
vectorizer=CountVectorizer()
transformer = TfidfTransformer()
tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus)) 
print('============== tfidf ===================')
print(tfidf)
'''
# Output
============== tfidf ===================
  (0, 8)        0.438776742859
  (0, 3)        0.438776742859
  (0, 6)        0.358728738248
  (0, 2)        0.541976569726
  (0, 1)        0.438776742859
  (1, 8)        0.272301467523
  (1, 3)        0.272301467523
  (1, 6)        0.222624292325
  (1, 1)        0.272301467523
  (1, 5)        0.853225736145
  (2, 6)        0.28847674875
  (2, 0)        0.552805319991
  (2, 7)        0.552805319991
  (2, 4)        0.552805319991
  (3, 8)        0.438776742859
  (3, 3)        0.438776742859
  (3, 6)        0.358728738248
  (3, 2)        0.541976569726
  (3, 1)        0.438776742859
'''

当然也可以直接使用 TfidfVectorizer 直接得到最后的结果。

 
from sklearn.feature_extraction.text import TfidfVectorizer
 
corpus = [          'This is the first document.',
		'This is the second second document.',
		'And the third one.',
		'Is this the first document?',
		]
 
tfidf_vectorizer = TfidfVectorizer() 
tfidf = tfidf_vectorizer.fit_transform(corpus)
print('============== tfidf ===================')
print(tfidf)
'''
# Output
============== tfidf ===================
  (0, 8)        0.438776742859
  (0, 3)        0.438776742859
  (0, 6)        0.358728738248
  (0, 2)        0.541976569726
  (0, 1)        0.438776742859
  (1, 8)        0.272301467523
  (1, 3)        0.272301467523
  (1, 6)        0.222624292325
  (1, 1)        0.272301467523
  (1, 5)        0.853225736145
  (2, 6)        0.28847674875
  (2, 0)        0.552805319991
  (2, 7)        0.552805319991
  (2, 4)        0.552805319991
  (3, 8)        0.438776742859
  (3, 3)        0.438776742859
  (3, 6)        0.358728738248
  (3, 2)        0.541976569726
  (3, 1)        0.438776742859
'''

 

参考:

1. https://blog.csdn.net/unixtch/article/details/76685429

2.https://blog.csdn.net/ZenG_xiangt/article/details/81807890

3.https://blog.csdn.net/u011955252/article/details/52045392

4.https://blog.csdn.net/a40850273/article/details/81119103

5. https://www.jianshu.com/p/dcc27a28b3f4 (重点)

6.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值