处理文本数据(下):

4、停用词

删除没有信息量的单词还有另一种方法,就是舍弃那些出现次数太多以至于没有信息量的单词。有两种主要方法:使用特定语言的停用词(stopword)列表,或者舍弃那些出现过于频繁的单词。

  • scikit-learn 的 feature_extraction.text 模块中提供了英语停用词的内置列表:

  from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS

  print("Number of stop words: {}".format(len(ENGLISH_STOP_WORDS)))
  print("Every 10th stopword:\n{}".format(list(ENGLISH_STOP_WORDS)[::10]))


  # 指定 stop_words="english" 将使用内置列表
  # 我们也可以扩展这个列表并传入我们自己的列表
  vect = CountVectorizer(min_df=5, stop_words="english").fit(text_train)
  X_train = vect.transform(text_train)
  param_grid = {'C': [0.001, 0.01, 0.1, 1, 10]}

  print("X_train with stop words:\n{}".format(repr(X_train)))

  '''
  <25000x26966 sparse matrix of type '<class 'numpy.int64'>'
  	with 2149958 stored elements in Compressed Sparse Row format>
  '''


  #现在数据集中的特征数量减少了 305 个(27271-26966),说明大部分停用词(但不是所有)都出现了。我们再次运行网格搜索:

  grid = GridSearchCV(LogisticRegression(), param_grid, cv=5)
  grid.fit(X_train, y_train)
  print("Best cross-val上idation score: {:.2f}".format(grid.best_score_))
  # Best cross-validation score: 0.88

5、用 tf-idf 缩放数据

另一种方法是按照我们预计的特征信息量大小来缩放特征,而不是舍弃那些认为不重要的特征。
最常见的一种做法就是使用词频 - 逆向文档频率(term frequency–inverse document frequency,tf-idf)方法。

这一方法对在某个特定文档中经常出现的术语给予很高的权重,但对在语料库的许多文档中都经常出现的术语给予的权重却不高。
如果一个单词在某个特定文档中经常出现,但在许多文档中却不常出现,那么这个单词很可能是对文档内容的很好描述。

scikit-learn 在两个类中实现了 tf-idf 方法:TfidfTransformer 和 TfidfVectorizer,前者接受 CountVectorizer 生成的稀疏矩阵并将其变换,后者接受文本数据并完成词袋特征提取与 tf-idf 变换。

由于 tf-idf 实际上利用了训练数据的统计学属性,所以我们将使用管 道,以确保网格搜索的结果有效。这样会得到下列代码:

  from sklearn.feature_extraction.text import TfidfVectorizer
  from sklearn.pipeline import make_pipeline


  pipe = make_pipeline(TfidfVectorizer(min_df=5, norm=None),
                       LogisticRegression())
  param_grid = {'logisticregression__C': [0.001, 0.01, 0.1, 1, 10]}

  grid = GridSearchCV(pipe, param_grid, cv=5)
  grid.fit(text_train, y_train)
  print("Best cross-validation score: {:.2f}".format(grid.best_score_))
  # Best cross-validation score: 0.89

如你所见,使用 tf-idf 代替仅统计词数对性能有所提高。我们还可以查看 tf-idf 找到的最重要的单词。请记住,tf-idf 缩放的目的是找到能够区分文档的单词,但它完全是一种无监督技术。

因此,这里的 “重要” 不一定与我们感兴趣的 “正面评论” 和 “负面评论” 标签相关。

首先,我们从管道中提取 TfidfVectorizer:

  vectorizer = grid.best_estimator_.named_steps["tfidfvectorizer"]
  # 变换训练数据集
  X_train = vectorizer.transform(text_train)
  # 找到数据集中每个特征的最大值
  max_value = X_train.max(axis=0).toarray().ravel()
  sorted_by_tfidf = max_value.argsort()
  # 获取特征名称
  feature_names = np.array(vectorizer.get_feature_names())

  print("Features with lowest tfidf:\n{}".format(feature_names[sorted_by_tfidf[:20]]))
  '''
  Features with lowest tfidf:
  ['poignant' 'disagree' 'instantly' 'importantly' 'lacked' 'occurred'
   'currently' 'altogether' 'nearby' 'undoubtedly' 'directs' 'fond' 'stinker'
   'avoided' 'emphasis' 'commented' 'disappoint' 'realizing' 'downhill'
   'inane']
  '''
  print("Features with highest tfidf: \n{}".format(feature_names[sorted_by_tfidf[-20:]]))
  '''
  Features with highest tfidf: 
  ['coop' 'homer' 'dillinger' 'hackenstein' 'gadget' 'taker' 'macarthur'
   'vargas' 'jesse' 'basket' 'dominick' 'the' 'victor' 'bridget' 'victoria'
   'khouri' 'zizek' 'rob' 'timon' 'titanic']
  '''

tf-idf 较小的特征要么是在许多文档里都很常用,要么就是很少使用,且仅出现在非常长的文档中。

有趣的是,许多 tf-idf 较大的特征实际上对应的是特定的演出或电影。这些术语仅出现在这些特定演出或电影的评论中,但往往在这些评论中多次出现。例如,对于 “pokemon”、“smallville” 和 “doodlebops” 是显而易见的,但这里的 “scanners” 实际上指的也是电影标题。
这些单词不太可能有助于我们的情感分类任务(除非有些电影的评价可 能普遍偏正面或偏负面),但肯定包含了关于评论的大量具体信息。

6、研究模型系数

最后,我们详细看一下 Logistic 回归模型从数据中实际学到的内容。由于特征数量非常多 (删除出现次数不多的特征之后还有 27 271 个),所以显然我们不能同时查看所有系数。但 是,我们可以查看最大的系数,并查看这些系数对应的单词。我们将使用基于 tf-idf 特征训练的最后一个模型。

下面这张条形图给出了 Logistic 回归模型中最大的 25 个系数与最小的 25 个系数,其高度表示每个系数的大小:

  import mglearn

  mglearn.tools.visualize_coefficients(
      grid.best_estimator_.named_steps["logisticregression"].coef_,
      feature_names, n_top_features=40)

左侧的负系数属于模型找到的表示负面评论的单词,而右侧的正系数属于模型找到的表示正面评论的单词。

大多数单词都是非常直观的,比如 “worst”(最差)、“waste”(浪费)、“disappointment”(失望)和 “laughable”(可笑)都表示不好的电影评论,而 “excellent”(优秀)、“wonderful”(精彩)、“enjoyable”(令人愉悦)和 “refreshing” (耳目一新)则表示正面的电影评论。

有些词的含义不那么明确,比如 “bit”(一点)、 “job”(工作)和 “today”(今天),但它们可能是类似 “good job”(做得不错)和 “best today”(今日最佳)等短语的一部分。

7、多个单词的词袋(n 元分组)

使用词袋表示的主要缺点之一是完全舍弃了单词顺序。

因此,“it’s bad, not good at all”(电影很差,一点也不好)和 “it’s good, not bad at all”(电影很好,还不错)这两个字符串的词袋表示完全相同,尽管它们的含义相反。

将 “not”(不)放在单词前面,这只是上下文很重要的一个例子(可能是一个极端的例子)。

幸运的是,使用词袋表示时有一种获取上下文的方法,就是不仅考虑单一词例的计数,而且还考虑相邻的两个或三个词例的计数。

两个词例被称为二元分词(bigram),三个词例被称为三元分词(trigram),更一般的词例序列被称为 n 元分词(n-gram)。

我们可以通过改变 CountVectorizer 或 TfidfVectorizer 的 ngram_range 参数来改变作为特征的词例范围。

  #我们在 IMDb 电影评论数据上尝试使用 TfidfVectorizer,并利用网格搜索找出 n 元分词的最佳设置:

  pipe = make_pipeline(TfidfVectorizer(min_df=5), LogisticRegression())
  # 运行网格搜索需要很长的时间,因为网格相对较大,且包含三元分组
  param_grid = {'logisticregression__C': [0.001, 0.01, 0.1, 1, 10, 100],
                "tfidfvectorizer__ngram_range": [(1, 1), (1, 2), (1, 3)]}

  grid = GridSearchCV(pipe, param_grid, cv=5)
  grid.fit(text_train, y_train)
  print("Best cross-validation score: {:.2f}".format(grid.best_score_))
  # Best cross-validation score: 0.91
  print("Best parameters:\n{}".format(grid.best_params_))
  '''
  Best parameters:
  {'logisticregression__C': 100, 'tfidfvectorizer__ngram_range': (1, 3)}
  '''


  #将交叉验证精度作为 ngram_range 和 C 参数的函数并用热图可视化:

  # 从网格搜索中提取分数
  scores = grid.cv_results_['mean_test_score'].reshape(-1, 3).T
  # 热图可视化
  heatmap = mglearn.tools.heatmap(
      scores, xlabel="C", ylabel="ngram_range", cmap="viridis", fmt="%.3f",
      xticklabels=param_grid['logisticregression__C'],
      yticklabels=param_grid['tfidfvectorizer__ngram_range'])
  plt.colorbar(heatmap)

从热图中可以看出,使用二元分词对性能有很大提高,而添加三元分词对精度只有很小贡献。

为了更好地理解模型是如何改进的,我们可以将最佳模型的重要系数可视化,其中包含一元分词、二元分词和三元分词:

8、主题建模与文档聚类

常用于文本数据的一种特殊技术是主题建模(topic modeling),这是描述将每个文档分配给一个或多个主题的任务(通常是无监督的)的概括性术语。这方面一个很好的例子是新闻数据,它们可以被分为 “政治” “体育” “金融” 等主题。

8.1、隐含狄利克雷分布

LDA 模型试图找出频繁共同出现的单词群组(即主题)。LDA 还要求,每个文档可以被理解为主题子集的 “混合”。重要的是要理解,机器学习模型所谓的 “主题” 可能不是我们通常在日常对话中所说的主题,而是更类似于 PCA 或 NMF所提取的成分,它可能具有语义,也可能没有。

我们将 LDA 应用于电影评论数据集,来看一下它在实践中的效果。对于无监督的文本文档模型,通常最好删除非常常见的单词,否则它们可能会支配分析过程。我们将删除至少在 15% 的文档中出现过的单词,并在删除前 15% 之后,将词袋模型限定为最常见的 10 000 个单词:

  vect = CountVectorizer(max_features=10000, max_df=.15)
  X = vect.fit_transform(text_train)

  from sklearn.decomposition import LatentDirichletAllocation
  lda = LatentDirichletAllocation(n_topics=10, learning_method="batch",
                                  max_iter=25, random_state=0)
  #我们在一个步骤中构建模型并变换数据
  #计算变换需要花点时间,二者同时进行可以节省时间
  document_topics = lda.fit_transform(X)


  print("lda.components_.shape: {}".format(lda.components_.shape))
  
  '''
  lda.components_.shape: (10, 10000)
  '''
  #对于每个主题(components_的一行),将特征排序(升序)
  #用[:, ::-1]将行反转,使排序变为降序
  sorting = np.argsort(lda.components_, axis=1)[:, ::-1]
  #从向量器中获取特征名称
  feature_names = np.array(vect.get_feature_names())

  #打印出前10个主题:
  mglearn.tools.print_topics(topics=range(10), feature_names=feature_names,
                             sorting=sorting, topics_per_chunk=5, n_words=10)
  
  '''
  '''
  topic 0       topic 1       topic 2       topic 3       topic 4       
  --------      --------      --------      --------      --------      
  between       war           funny         show          didn          
  family        world         comedy        series        saw           
  young         us            guy           episode       thought       
  real          american      laugh         tv            am            
  us            our           jokes         episodes      thing         
  director      documentary   fun           shows         got           
  work          history       humor         season        10            
  both          years         re            new           want          
  beautiful     new           hilarious     years         going         
  each          human         doesn         television    watched       


  topic 5       topic 6       topic 7       topic 8       topic 9       
  --------      --------      --------      --------      --------      
  action        kids          role          performance   horror        
  effects       action        cast          role          house         
  nothing       animation     john          john          killer        
  budget        children      version       actor         gets          
  script        game          novel         cast          woman         
  minutes       disney        director      plays         dead          
  original      fun           both          jack          girl          
  director      old           played        michael       around        
  least         10            mr            oscar         goes          
  doesn         kid           young         father        wife
  '''
  '''

  lda100 = LatentDirichletAllocation(n_topics=100, learning_method="batch",
                                     max_iter=25, random_state=0)
  document_topics100 = lda100.fit_transform(X)

  #查看所有 100 个主题可能有点困难,所以我们选取了一些有趣的而且有代表性的主题:

  topics = np.array([7, 16, 24, 25, 28, 36, 37, 41, 45, 51, 53, 54, 63, 89, 97])
  sorting = np.argsort(lda100.components_, axis=1)[:, ::-1]
  feature_names = np.array(vect.get_feature_names())
  mglearn.tools.print_topics(topics=topics, feature_names=feature_names,
                             sorting=sorting, topics_per_chunk=5, n_words=20)

  '''
  '''
  topic 7       topic 16      topic 24      topic 25      topic 28      
  --------      --------      --------      --------      --------      
  thriller      worst         german        car           beautiful     
  suspense      awful         hitler        gets          young         
  horror        boring        nazi          guy           old           
  atmosphere    horrible      midnight      around        romantic      
  mystery       stupid        joe           down          between       
  house         thing         germany       kill          romance       
  director      terrible      years         goes          wonderful     
  quite         script        history       killed        heart         
  bit           nothing       new           going         feel          
  de            worse         modesty       house         year          
  performances  waste         cowboy        away          each          
  dark          pretty        jewish        head          french        
  twist         minutes       past          take          sweet         
  hitchcock     didn          kirk          another       boy           
  tension       actors        young         getting       loved         
  interesting   actually      spanish       doesn         girl          
  mysterious    re            enterprise    now           relationship  
  murder        supposed      von           night         saw           
  ending        mean          nazis         right         both          
  creepy        want          spock         woman         simple        


  topic 36      topic 37      topic 41      topic 45      topic 51      
  --------      --------      --------      --------      --------      
  performance   excellent     war           music         earth         
  role          highly        american      song          space         
  actor         amazing       world         songs         planet        
  cast          wonderful     soldiers      rock          superman      
  play          truly         military      band          alien         
  actors        superb        army          soundtrack    world         
  performances  actors        tarzan        singing       evil          
  played        brilliant     soldier       voice         humans        
  supporting    recommend     america       singer        aliens        
  director      quite         country       sing          human         
  oscar         performance   americans     musical       creatures     
  roles         performances  during        roll          miike         
  actress       perfect       men           fan           monsters      
  excellent     drama         us            metal         apes          
  screen        without       government    concert       clark         
  plays         beautiful     jungle        playing       burton        
  award         human         vietnam       hear          tim           
  work          moving        ii            fans          outer         
  playing       world         political     prince        men           
  gives         recommended   against       especially    moon          


  topic 53      topic 54      topic 63      topic 89      topic 97      
  --------      --------      --------      --------      --------      
  scott         money         funny         dead          didn          
  gary          budget        comedy        zombie        thought       
  streisand     actors        laugh         gore          wasn          
  star          low           jokes         zombies       ending        
  hart          worst         humor         blood         minutes       
  lundgren      waste         hilarious     horror        got           
  dolph         10            laughs        flesh         felt          
  career        give          fun           minutes       part          
  sabrina       want          re            body          going         
  role          nothing       funniest      living        seemed        
  temple        terrible      laughing      eating        bit           
  phantom       crap          joke          flick         found         
  judy          must          few           budget        though        
  melissa       reviews       moments       head          nothing       
  zorro         imdb          guy           gory          lot           
  gets          director      unfunny       evil          saw           
  barbra        thing         times         shot          long          
  cast          believe       laughed       low           interesting   
  short         am            comedies      fulci         few           
  serial        actually      isn           re            half
  '''
  '''

在没有标签的情况下(或者像本章的例子这样,即使有标签的情况下),像 LDA 这样的主 题模型是理解大型文本语料库的有趣方法。不过 LDA 算法是随机的,改变 random_state 参数可能会得到完全不同的结果。虽然找到主题可能很有用,但对于从无监督模型中得出的任何结论都应该持保留态度,我们建议通过查看特定主题中的文档来验证你的直觉。 LDA.transform 方法生成的主题有时也可以用于监督学习的紧凑表示。当训练样例很少时, 这一方法特别有用。

参考文献

《机器学习基础教程》

posted @ 2022-06-04 11:58  朝南烟  阅读(157)  评论(0编辑  收藏  举报
body { color: #000; background-color: #e6e6e6; font-family: "Helvetica Neue",Helvetica,Verdana,Arial,sans-serif; font-size: 12px; min-height: 101%; background: url(https://images.cnblogs.com/cnblogs_com/caolanying/1841633/o_2009041…ly1geq8oc9owbj21hc0u0th5.jpg) fixed; } #home { margin: 0 auto; opacity: 0.8; width: 65%; min-width: 1080px; background-color: #fff; padding: 30px; margin-top: 50px; margin-bottom: 50px; box-shadow: 0 2px 6px rgba(100, 100, 100, 0.3); }