传统nlp任务处理文本及其依赖已有的词表,只有在词表里出现的词才能被识别并加以处理。但这也带来了一些问题:
假设没有词表,如何从文本中发现新词?
随着时间推移,新词会不断出现,固有词表会过时,怎么维护词表?
新词发现(如何判定一个词?)
词相当于一种固定搭配
词的内部应该是稳固的——内部凝固度
词的外部应该是多变的——左右熵
从词到理解
有了分词能力后,需要利用词来完成对文本的理解;
首先可以想到的,就是从文章里挑选重要词。
何为重要词
假设一个词在某类文本中(假设为A类)出现的次数很多,而在其他类别文本(非A类)出现很少,那么这个词是A类文本的重要词(高权重词)。恒星、黑洞——>天文。
反之,若一个词在很多领域都有出现,则其对于任意类别的重要性都很差。你好、谢谢——> ???
如何从数学角度刻画
一种NLP的经典统计值:TF·IDF
TF:词频,某个词在某类别中出现的次数/该类别的词总数
IDF:逆文档频率。逆文档频率高——>该词很少出现在其他文档。
TF·IDF计算
TF·IDF = TF * IDF
假设有四篇文档,文档中的词用字母替代
A: a b c d a b c d
B: b c b c b c
C: b d b d
D: d d d d d d d d
每个词对于每个类别都会得到一个TF·IDF值
TF·IDF高——>该词对于该领域重要程度高,低则相反
算法特点
1、tfidf的计算非常依赖分词结果,如果分词出错,统计值的意义会大打折扣。
2、每个词,对于每篇文档,有不同的tfidf值,所以不能脱离数据讨论tfidf。
3、如果只有一篇文本,不能计算tfidf。
4、类别数据均衡很重要。
5、容易受各种符号影响,最好做一些预处理。
TFIDF应用——搜索引擎
1、对于已有的所有网页(文本),计算每个网页中词的TFIDF值。
2、对于一个输入query进行分词。
3、对于文档D,计算query中的词在文档D中的TFIDF值总和,作为query和文档的相关性得分。
import jieba
import math
import os
import json
from collections import defaultdict
from calculate_tfidf import calculate_tfidf, tf_idf_topk
"""
基于tfidf实现简单搜索引擎
"""jieba.initialize()#加载文档数据(可以想象成网页数据),计算每个网页的tfidf字典
def load_data(file_path):corpus = []with open(file_path, encoding="utf8") as f:documents = json.loads(f.read())for document in documents:corpus.append(document["title"] + "\n" + document["content"])tf_idf_dict = calculate_tfidf(corpus)return tf_idf_dict, corpusdef search_engine(query, tf_idf_dict, corpus, top=3):query_words = jieba.lcut(query)res = []for doc_id, tf_idf in tf_idf_dict.items():score = 0for word in query_words:score += tf_idf.get(word, 0)res.append([doc_id, score])res = sorted(res, reverse=True, key=lambda x:x[1])for i in range(top):doc_id = res[i][0]print(corpus[doc_id])print("--------------")return resif __name__ == "__main__":path = "news.json"tf_idf_dict, corpus = load_data(path)while True:query = input("请输入您要搜索的内容:")search_engine(query, tf_idf_dict, corpus)
TFIDF应用——文本摘要
1、通过计算TFIDF值得到每个文本的关键词
2、将包含关键词多的句子,认为是关键句。
3、挑选若干关键句,作为文本的摘要。
import jieba
import math
import os
import random
import re
import json
from collections import defaultdict
from calculate_tfidf import calculate_tfidf, tf_idf_topk
"""
基于tfidf实现简单文本摘要
"""jieba.initialize()#加载文档数据(可以想象成网页数据),计算每个网页的tfidf字典
def load_data(file_path):corpus = []with open(file_path, encoding="utf8") as f:documents = json.loads(f.read())for document in documents:assert "\n" not in document["title"]assert "\n" not in document["content"]corpus.append(document["title"] + "\n" + document["content"])tf_idf_dict = calculate_tfidf(corpus)return tf_idf_dict, corpus#计算每一篇文章的摘要
#输入该文章的tf_idf词典,和文章内容
#top为人为定义的选取的句子数量
#过滤掉一些正文太短的文章,因为正文太短在做摘要意义不大
def generate_document_abstract(document_tf_idf, document, top=3):sentences = re.split("?|!|。", document)#过滤掉正文在五句以内的文章if len(sentences) <= 5:return Noneresult = []for index, sentence in enumerate(sentences):sentence_score = 0words = jieba.lcut(sentence)for word in words:sentence_score += document_tf_idf.get(word, 0)sentence_score /= (len(words) + 1)result.append([sentence_score, index])result = sorted(result, key=lambda x:x[0], reverse=True)#权重最高的可能依次是第10,第6,第3句,将他们调整为出现顺序比较合理,即3,6,10important_sentence_indexs = sorted([x[1] for x in result[:top]])return "。".join([sentences[index] for index in important_sentence_indexs])#生成所有文章的摘要
def generate_abstract(tf_idf_dict, corpus):res = []for index, document_tf_idf in tf_idf_dict.items():title, content = corpus[index].split("\n")abstract = generate_document_abstract(document_tf_idf, content)if abstract is None:continuecorpus[index] += "\n" + abstractres.append({"标题":title, "正文":content, "摘要":abstract})return resif __name__ == "__main__":path = "news.json"tf_idf_dict, corpus = load_data(path)res = generate_abstract(tf_idf_dict, corpus)writer = open("abstract.json", "w", encoding="utf8")writer.write(json.dumps(res, ensure_ascii=False, indent=2))writer.close()
TFIDF应用——文本相似度计算
1、对所有文本计算TFIDF后,从每个文本选取tfidf较高的前n个词,得到一个词的集合S。
2、对于每篇文本D,计算S中的每个词的词频,将其作为文本的向量。
3、通过计算向量夹角余弦值,得到向量相似度,作为文本的相似度。
向量夹角余弦值计算:
#coding:utf8
import jieba
import math
import os
import json
from collections import defaultdict
from calculate_tfidf import calculate_tfidf, tf_idf_topk"""
基于tfidf实现文本相似度计算
"""jieba.initialize()#加载文档数据(可以想象成网页数据),计算每个网页的tfidf字典
#之后统计每篇文档重要在前10的词,统计出重要词词表
#重要词词表用于后续文本向量化
def load_data(file_path):corpus = []with open(file_path, encoding="utf8") as f:documents = json.loads(f.read())for document in documents:corpus.append(document["title"] + "\n" + document["content"])tf_idf_dict = calculate_tfidf(corpus)topk_words = tf_idf_topk(tf_idf_dict, top=5, print_word=False)vocab = set()for words in topk_words.values():for word, score in words:vocab.add(word)print("词表大小:", len(vocab))return tf_idf_dict, list(vocab), corpus#passage是文本字符串
#vocab是词列表
#向量化的方式:计算每个重要词在文档中的出现频率
def doc_to_vec(passage, vocab):vector = [0] * len(vocab)passage_words = jieba.lcut(passage)for index, word in enumerate(vocab):vector[index] = passage_words.count(word) / len(passage_words)return vector#先计算所有文档的向量
def calculate_corpus_vectors(corpus, vocab):corpus_vectors = [doc_to_vec(c, vocab) for c in corpus]return corpus_vectors#计算向量余弦相似度
def cosine_similarity(vector1, vector2):x_dot_y = sum([x*y for x, y in zip(vector1, vector2)])sqrt_x = math.sqrt(sum([x ** 2 for x in vector1]))sqrt_y = math.sqrt(sum([x ** 2 for x in vector2]))if sqrt_y == 0 or sqrt_y == 0:return 0return x_dot_y / (sqrt_x * sqrt_y + 1e-7)#输入一篇文本,寻找最相似文本
def search_most_similar_document(passage, corpus_vectors, vocab):input_vec = doc_to_vec(passage, vocab)result = []for index, vector in enumerate(corpus_vectors):score = cosine_similarity(input_vec, vector)result.append([index, score])result = sorted(result, reverse=True, key=lambda x:x[1])return result[:4]if __name__ == "__main__":path = "news.json"tf_idf_dict, vocab, corpus = load_data(path)corpus_vectors = calculate_corpus_vectors(corpus, vocab)passage = "魔兽争霸"for corpus_index, score in search_most_similar_document(passage, corpus_vectors, vocab):print("相似文章:\n", corpus[corpus_index].strip())print("得分:", score)print("--------------")
TFIDF的优势
1、可解释性好:
可以清晰地看到关键词,即使预测出错,也很容易就找到原因。
2、计算速度快:
分词本身占耗时最多,其余为简单统计计算
3、对标注数据依赖小
可以使用无标注预料完成一部分工作
4、可以与很多算法组合使用
可以看做是词的权重
TFIDF的劣势
1、受分词效果影响大
2、词与词之间没有语义相似度
3、没有语序信息(词袋模型)
4、能力范围有限,无法完成复杂任务,如机器翻译和实体挖掘等
5、样本不均衡会对结果有很大影响
6、类内样本间分部不被考虑