文本数值表示的一种实用方法是使用标准Python库,特别是scikit-learn,生成TF-IDF特征并加入N-gram。假设您已安装scikit-learn,并拥有一个可用的Python环境。环境与数据准备首先,我们导入必要的工具:来自scikit-learn的TfidfVectorizer。我们还将定义一个小样本语料库以便操作。from sklearn.feature_extraction.text import TfidfVectorizer import pandas as pd # 样本文档 corpus = [ "The quick brown fox jumps over the lazy dog.", "The lazy dog slept in the sun.", "The quick brown cat ran away.", "Never jump over the lazy dog quickly!" # 添加变体 ] # 可选:显示语料库 print("样本语料库:") for i, doc in enumerate(corpus): print(f"文档 {i+1}: {doc}")这个简单语料库包含四个短文档。我们的目标是将这些文本字符串转换为一个矩阵,其中行表示文档,列表示特征(经TF-IDF加权的词语)。生成TF-IDF特征TfidfVectorizer将分词、计数和TF-IDF转换组合为一个对象。我们来初始化它并将其拟合到我们的语料库。# 1. 初始化TfidfVectorizer # 默认设置:lowercase=True, token_pattern=r"(?u)\b\w\w+\b", stop_words=None, ngram_range=(1, 1) tfidf_vectorizer = TfidfVectorizer() # 2. 将向量器拟合到语料库并转换数据 # fit_transform() 学习词汇表和IDF,然后返回TF-IDF矩阵 tfidf_matrix = tfidf_vectorizer.fit_transform(corpus) # 输出为稀疏矩阵(对高维度数据高效) print("\nTF-IDF矩阵的形状:", tfidf_matrix.shape) # 输出:TF-IDF矩阵的形状:(4, 15) # 这表示在默认处理后,词汇表中包含4个文档和15个独立词语(特征)。fit_transform方法首先学习词汇表(所有符合条件的独立词元),并计算整个语料库中每个词语的逆文档频率(IDF)。然后,它将语料库转换为文档-词语矩阵,其中每个单元格包含特定词语在特定文档中的TF-IDF分数。生成的tfidf_matrix通常是SciPy稀疏矩阵。这种方式内存高效,因为文档-词语矩阵中的大多数条目都是零(大多数词语不会出现在大多数文档中)。为了更好地理解这个矩阵,我们来看看学习到的词汇表以及(对于小型示例而言)矩阵的稠密表示。# 获取词汇表(词语到列索引的映射) feature_names = tfidf_vectorizer.get_feature_names_out() print("\n词汇表(特征名称):") print(feature_names) # Output: ['away' 'brown' 'cat' 'dog' 'fox' 'in' 'jumps' 'lazy' 'never' 'over' 'quick' 'quickly' 'ran' 'slept' 'sun' 'the'] # 注意:如果启用默认词元模式或停用词,'is'、'a'等词可能会被移除。 # 为了本示例的清晰度,我们不使用隐式停用词移除功能重新运行: # tfidf_vectorizer = TfidfVectorizer(stop_words=None) # tfidf_matrix = tfidf_vectorizer.fit_transform(corpus) # feature_names = tfidf_vectorizer.get_feature_names_out() # 此输出假设使用默认分词且向量器初始化时未显式移除停用词 # 将稀疏矩阵转换为稠密NumPy数组以便检查 dense_tfidf_matrix = tfidf_matrix.toarray() # 以DataFrame形式显示,提高可读性 df_tfidf = pd.DataFrame(dense_tfidf_matrix, columns=feature_names, index=[f"Doc_{i+1}" for i in range(len(corpus))]) print("\nTF-IDF矩阵(稠密):") print(df_tfidf.round(2)) # 为显示目的四舍五入您会注意到,许多文档中常见的词语(如“the”)其IDF值倾向于较低,这可能导致其TF-IDF分数低于更稀有、更具区分度的词语(如“fox”或“cat”),即使它们在文档中的词频(TF)相似。例如,“lazy”和“dog”出现在三个文档中,而“fox”仅出现在一个文档中。它们的TF-IDF分数将反映出文档频率的这种差异。我们来可视化几个选定词语在这些文档中的TF-IDF分数。# 选择几个词语进行可视化 terms_to_plot = ['dog', 'fox', 'lazy', 'quick', 'the'] term_indices = [list(feature_names).index(term) for term in terms_to_plot if term in feature_names] term_labels = [feature_names[i] for i in term_indices] # 提取这些词语的分数 scores_to_plot = dense_tfidf_matrix[:, term_indices] # 为Plotly柱状图创建数据 plot_data = [] colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd'] # Plotly默认颜色 for i, term in enumerate(term_labels): plot_data.append({ "x": [f"Doc_{j+1}" for j in range(len(corpus))], "y": scores_to_plot[:, i], "name": term, "type": "bar", "marker": {"color": colors[i % len(colors)]} }) {"layout": {"title": "选定词语的TF-IDF分数", "xaxis": {"title": "文档"}, "yaxis": {"title": "TF-IDF分数"}, "barmode": "group", "height": 400, "width": 600, "margin": {"l": 50, "r": 20, "t": 50, "b": 50}}, "data": [{"x": ["Doc_1", "Doc_2", "Doc_3", "Doc_4"], "y": [0.30131993, 0.43896731, 0.0, 0.36868386], "name": "dog", "type": "bar", "marker": {"color": "#228be6"}}, {"x": ["Doc_1", "Doc_2", "Doc_3", "Doc_4"], "y": [0.38353754, 0.0, 0.0, 0.0], "name": "fox", "type": "bar", "marker": {"color": "#fd7e14"}}, {"x": ["Doc_1", "Doc_2", "Doc_3", "Doc_4"], "y": [0.30131993, 0.43896731, 0.0, 0.36868386], "name": "lazy", "type": "bar", "marker": {"color": "#40c057"}}, {"x": ["Doc_1", "Doc_2", "Doc_3", "Doc_4"], "y": [0.38353754, 0.0, 0.50091563, 0.0], "name": "quick", "type": "bar", "marker": {"color": "#f03e3e"}}, {"x": ["Doc_1", "Doc_2", "Doc_3", "Doc_4"], "y": [0.22746677, 0.33128776, 0.37689599, 0.27729131], "name": "the", "type": "bar", "marker": {"color": "#7048e8"}}]}词语“dog”、“fox”、“lazy”、“quick”和“the”在四个样本文档中的TF-IDF分数。注意“the”尽管出现频繁,但得分相对较低,而“fox”仅在出现它的文档1中得分较高。“dog”和“lazy”因常共同出现而具有相似模式。加入N-gram标准TF-IDF(使用一元词,即单个词语)会丢失词语顺序信息。“quick brown fox”和“fox brown quick”将具有相同的表示。N-gram通过将词语序列作为特征来帮助捕捉局部语境。我们可以使用ngram_range参数配置TfidfVectorizer以包含N-gram。它接受一个元组(min_n, max_n)。例如,ngram_range=(1, 2)会生成一元词和二元词。# 初始化向量器以包含一元词和二元词 ngram_vectorizer = TfidfVectorizer(ngram_range=(1, 2)) # 生成1-gram和2-gram # 拟合和转换 ngram_tfidf_matrix = ngram_vectorizer.fit_transform(corpus) # 检查新形状和部分特征名称 print("\n带N-gram (1, 2) 的TF-IDF矩阵形状:", ngram_tfidf_matrix.shape) # 输出:带N-gram (1, 2) 的TF-IDF矩阵形状:(4, 31) -> 现在特征更多了! ngram_feature_names = ngram_vectorizer.get_feature_names_out() print("\nN-gram特征示例:") # 为说明目的,打印前10个和后10个特征 print(list(ngram_feature_names[:10]) + list(ngram_feature_names[-10:])) # 输出包含“brown fox”、“lazy dog”、“quick brown”、“the lazy”等特征。您可以看到,加入N-gram后特征数量显著增加。这能捕捉更多语境(例如,区分“lazy dog”与“lazy”和“dog”分别出现的情况),但同时也增加了特征空间的维度。这可能使计算更耗时,并且有时需要更多数据以避免过拟合。通常的做法是使用一元词和二元词(ngram_range=(1, 2)),偶尔也使用三元词(ngram_range=(1, 3)),这取决于具体的任务和语料库大小。您可以使用TfidfVectorizer中的max_features参数来限制特征的总数,该参数会只保留按整个语料库词频排序的靠前特征。# 示例:使用二元词同时限制特征数量 limited_ngram_vectorizer = TfidfVectorizer(ngram_range=(1, 2), max_features=20) limited_ngram_matrix = limited_ngram_vectorizer.fit_transform(corpus) print("\n带N-gram (1, 2) 且 max_features=20 的形状:", limited_ngram_matrix.shape) # 输出:带N-gram (1, 2) 且 max_features=20 的形状:(4, 20) limited_features = limited_ngram_vectorizer.get_feature_names_out() print("\n受限特征集示例:") print(limited_features[:10]) # 显示一些选定的靠前特征总结在本实践部分,您学会了如何:使用scikit-learn的TfidfVectorizer将原始文本文档转换为数值型TF-IDF矩阵。检查生成的稀疏矩阵,并理解其维度和词汇表。配置向量器以包含N-gram(如二元词),从而使用ngram_range参数捕捉局部词语顺序。认识到使用N-gram时语境捕捉与维度之间的权衡。可选地使用max_features限制特征数量。生成有意义的数值特征,例如可借由N-gram改进的TF-IDF向量,是为机器学习模型准备文本数据的一个基础步骤,我们将在下一章讨论这方面内容。