1-1 语料
# 实现词袋模型 tf-idf 的表现模式
corpus = ['我们都生活在阴沟里,但仍有人仰望星空',
          '每个圣人都有过去,每个罪人都有未来',
          ]
1-2 分词
import jieba
# 将停用词读出放在 stopwords 这个列表中
stopwords_filepath = r'stopwords.txt'
stopwords = [line.strip()for line in open(stopwords_filepath,'r',encoding='utf-8').readlines()]
# 拿到停用词列表后,开始分词
word_list = []
for corpu in corpus:
    seg_list = jieba.cut(corpu)
    seg_list = [i for i in seg_list if i not in stopwords and i!=' ']
    word_list.append(seg_list)
print(word_list)
# -->
# [['我们', '都', '生活', '在', '阴沟', '里', '但', '仍', '有人', '仰望', '星空'], ['每个', '圣人', '都', '有', '过去', '每个', '罪人', '都', '有', '未来']]
# 分词结果一般都是放在列表里的
1-3 词袋模型
# gensim 包底层是基于 numpy 的数据格式,相对于 numpy 封装了更多的计算方法和一些可以快捷实现的统计学方法
from gensim import corpora
# 制造词典,拿出分词结果
dictionary = corpora.Dictionary(word_list)
print(dictionary.token2id)      # 拿出数字到词的一一映射,也叫做词袋
# -->
# {'仍': 0, '仰望': 1, '但': 2, '在': 3, '我们': 4, '星空': 5, '有人': 6, '生活': 7, '都': 8, '里': 9, '阴沟': 10, '圣人': 11, '有': 12, '未来': 13, '每个': 14, '罪人': 15, '过去': 16}
# 这是拿出的词袋,现在想通过这个词袋,做词袋模型
# 循环分词后的列表,把每一个分词结果拿出来
# 然后将分词结果放到 dictionary.doc2bow 里面
# dictionary.doc2bow(word) 里的 word 是每一句话的分词结果;dictionary.doc2bow(word)方法会把分词结果变成词袋模型展现形式
# 然后通过列表生成式,将结果放到列表中
words_bag = [dictionary.doc2bow(word) for word in word_list]
print(words_bag)
# -->
# [[(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 1), (10, 1)], [(8, 2), (11, 1), (12, 2), (13, 1), (14, 2), (15, 1), (16, 1)]]
# 第一句话被分词 10 个词,0 代表 “仍” 出现了 1 次
# (1,1) 就是 '仰望' 出现了 1 次
# 这就是 稀疏表示的词向量模型

# 可以把稀疏表示变成稠密表示
# 比如 [(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 1), (10, 1)]
# 创建一个和词袋等长的,全是 0 的矩阵,把 映射的下标变成 1 得到
# [1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0] 这就是稠密表示的词向量模型
1-4 tfidf
from gensim import models
# 导入 tfidf 模型,然后传入 words_bag,words_bag 是稀疏表示的词袋模型
tfidf_model = models.TfidfModel(words_bag)      # 这个模型是默认被训练了的
tfidf_model.save("tfidf_model.bin")             # 保存模型
tfidf_model = models.TfidfModel.load("tfidf_model.bin")     # 加载模型
# print(tfidf_model)
# --> TfidfModel(num_docs=2, num_nnz=18)

tfidf_vec = []
for i in words_bag:
    # tfidf 只是一个模型,需要通过模型,取出内容;i 是每一个 词袋模型的稀疏表示
    # 返回的 one_line_tfidf 也是稀疏表示
    one_line_tfidf = tfidf_model[i]
    tfidf_vec.append(one_line_tfidf)
print(tfidf_vec)
# -->
# [[(0, 0.31622776601683794), (1, 0.31622776601683794), (2, 0.31622776601683794), (3, 0.31622776601683794), (4, 0.31622776601683794), (5, 0.31622776601683794), (6, 0.31622776601683794), (7, 0.31622776601683794), (9, 0.31622776601683794), (10, 0.31622776601683794)],
# [(11, 0.2886751345948129), (12, 0.5773502691896258), (13, 0.2886751345948129), (14, 0.5773502691896258), (15, 0.2886751345948129), (16, 0.2886751345948129)]]
# 打印出的结果 也是稀疏表示形式; 这里后面的结果是 tfdif 的值
# 这就是使用 gensim 包产生词袋模型和 tfidf 的表现形式
知识点说明:
1. 什么是词袋模型?
ont-hot 的表现形式,其实就是二进制的表现形式;这个映射表可以称之为词袋;ont-hot 的长度就是词袋的长度;
有了对每个词的表示后,如何理解句子呢?一般会用词袋模型表达对句子的理解。
每个词都可以用 ont-hot 的表现形式表示;这句话的表现形式,用count 统计,纵向相加;得出来的结果就是词袋模型,词袋模型的长度和词袋的长度是同长的,
如果这个词出现了,这个词所对应的索引的下标就不是0,因为表示的是次数,词袋模型可以统计当前句子里面出现了多少词以及词出现的词数;
但是缺点是会忽略词的顺序关系,也无法表现出每个词的重要程度,当然这只是一种基础的文本表示;
如果想表达顺序,应该如何表示?比如“每个”是在“圣人”前面的;可以用 N-gram 表达
1-1 什么是 ont-hot?
如何理解词意?最简单的理解方式是,给每个词一个唯一的索引,进行一一映射;
有了映射后,可以拿这些映射代表这些词,使用这些映射进行统计学的理解分析,也可以对词意进行理解
one-hot 就是把索引展开,用 0 1 的方式表示;
1-2 什么是 N-gram 模型?
N-gram 语言模型本质是依托于马尔科夫假设的模型,是语言模型的基础;
是在马尔科夫假设基础上,即假设当前词出现的概率只依赖于前 n-1 个词

1-3 什么是 N-gram 的词袋模型表现形式?
有顺序的把两个词放在一起建立词袋,通过这种词袋创建词袋模型,这个词袋模型具有简单的顺序关系;这就是 N-gram 的词袋模型表现形式;
但是这个用的并不多,平时用的最多的是词袋模型加 TF-IDF的表现模式
2. 什么是 tf-idf 表现形式?
TF-IDF 是表现一个词的重要程度的,一个词在文档中出现的次数越多,认为这个词越重要
但是总有些例外,比如“的” “地” “得”,“你” “我” “他”,所以需要进行一下中和
使用 IDF 进行中和操作,IDF用来判断词的稀有程度,
比如有一个词在某篇文档中出现了非常多的次数,而文档总数可能是2000,这个词只在其中 3 篇 文档中出现过
就是 2000/(3+1),加1 是为了防止文档数为0,是一种平滑操作;
这个词在非常少的文档中出现了,说明这个词非常稀有,这个稀有的词又在这篇文章中出现很多次,就说明这个词在这篇文章中非常重要。这个就是 TF-IDF
这也是 NLP 中,最基础的文本表现形式;虽然比较基础,但是用途非常广泛
将这个结果输入到机器学习或者深度学习里,就可以进行一些文本的分类,或者文本是否匹配等匹配任务

3. 为什么有稀疏表示和稠密表示?
数据量很大时,可以节省内存空间