机器学习之第一部分:词袋模型 博客分类: 机器学习
程序员文章站
2024-03-22 10:42:10
...
第一部分:词袋模型
什么是NLP?
NLP(自然语言处理)是一组接近文本的技术问题。这个页面将帮助您开始使用加载和清扫IMDB影评,然后应用一个简单的袋子的话模型得到惊人的准确的预测评估是否赞成或反对。
在你开始之前
本教程使用的Python语言。如果你还没有使用Python之前,我们建议在前往泰坦尼克号竞争Python教程把你的脚弄湿(查看随机森林介绍当你)。如果您已经熟悉Python和基本的NLP技术,您可能想要跳到第2部分。
这部分的教程不是依赖于平台。在本教程中我们将使用不同的Python模块用于文本处理,深度学习,随机森林,和其他应用程序。详细信息,请参阅设置您的系统页面。
确实有很多好的教程,整本书关于NLP和Python中的文本处理。本教程是绝不要详尽,只是为了帮助你开始电影评论。
Code
第1部分的教程代码住在这里。
读取数据
必要的文件可以从数据下载页面。第一个文件,你需要的是unlabeledTrainData.tsv,其中包含25000 IMDB影评,每一个都有积极或消极情绪标签。
接下来,读取的文件到Python一样。要做到这一点,我们可以使用pandas的package,介绍了《泰坦尼克号》教程,它提供的read_csv函数轻松地读取和写入数据文件。如果你还没有使用过pandas,您可能需要安装它。
# Import the pandas package, then use the "read_csv" function to read # the labeled training data import pandas as pd train = pd.read_csv("labeledTrainData.tsv", header=0, \ delimiter="\t", quoting=3) |
这里,“header= 0”表示该文件的第一行包含列名称,“delimiter=\t”表示字段由\t分割, quoting=3告诉Python忽略双引号,否则你可能试图读取文件会遇到错误。
我们可以确保我们读25000行3列如下:
>>> train.shape (25000, 3) >>> train.columns.values array([id, sentiment, review], dtype=object) |
三列被称为“id”、“sentiment”,和“review。“现在你已经阅读训练集,看看一些评论:
print train["review"][0] |
提醒一下,这将显示你第一个电影评论专栏名为“review。“你应该看到一个评论,开始是这样的:
"With all this stuff going down at the moment with MJ i've started listening to his music, watching the odd documentary here and there, watched The Wiz and watched Moonwalker again. Maybe i just want to get a certain insight into this guy who i thought was really cool in the eighties just to maybe make up my mind whether he is guilty or innocent. Moonwalker is part biography, part feature film which i remember going to see at the cinema when it was originally released. Some of it has subtle messages about MJ's feeling towards the press and also the obvious message of drugs are bad m'kay. <br/><br/>..." |
有HTML标签如“< br / >”、缩写词、标点符号——所有常见问题从网上当处理文本。花点时间浏览其他评论在训练集时,下一节将处理如何整理机器学习的文本。
数据清理和文本预处理
删除HTML标记:使用BeautifulSoup包
首先,我们将删除HTML标记。为了这个目的,我们将使用BeautifulSoup库。如果你还没安装BeautifulSoup,按照下面的步骤:
$ sudo pip install BeautifulSoup4 |
从命令行(而不是从Python内)。然后,加载Python包和使用它来提取文本的审查:
# Import BeautifulSoup into your workspace from bs4 import BeautifulSoup # Initialize the BeautifulSoup object on a single movie review example1 = BeautifulSoup(train["review"][0]) # Print the raw review and then the output of get_text(), for # comparison print train["review"][0] print example1.get_text() |
调用get_text()给你评论的文本,没有标签或标记。如果你浏览BeautifulSoup文档,你会发现这是一个非常强大的文档集——比我们需要更强大的数据集。然而,它是不被认为是一个可靠的实践删除标记使用正则表达式,所以即使是这么简单的应用程序,通常最好使用一种像BeautifulSoup包。
处理标点符号、数字和停顿词:NLTK和正则表达式
在考虑如何清洁文本时,我们应该想想我们试图解决的问题的数据。对许多问题,去掉标点符号。另一方面,在这种情况下,我们正在处理一个分析问题,它是可行的,”! ! !”或“:-(“可以携带观点,应该视为单词。在本教程中,为简单起见,我们去掉标点符号,但这是你可以自己玩。
同样,在本教程中我们将删除数字,但也有其他的方式处理他们一样意义。例如,我们可以把它们当作单词,或用一个占位符替换所有数字,如“NUM”。
删除标点符号和数字,我们将使用一个package来处理正则表达式,。包是用Python内置;不需要安装任何东西。正则表达式如何工作的详细描述,看到包的文档。现在,试试以下:
import re # Use regular expressions to do a find-and-replace letters_only = re.sub("[^a-zA-Z]", # The pattern to search for " ", # The pattern to replace it with example1.get_text() ) # The text to search print letters_only |
正则表达式的一个完整的概述是超出了本教程的范围,但是现在它就足以知道[]表示组成员和^表示“not”。换句话说,re.sub()语句上面说,“找到不是一个小写字母(a - z)或一个大写字母(a - z),用空格替换”。
我们也会把我们的评论小写并将它们分为单词(在NLP术语中称为“tokenization”):
lower_case = letters_only.lower() # Convert to lower case words = lower_case.split() # Split into words |
最后,我们需要决定如何应对频繁发生的词,没有太多的意义。这样的话被称为“停止词”;在英语词汇如“a”,其中包括“is”,“and“,”“。方便的,有Python包,带有内置的停止词列表。让我们导入一个阻止单词列表Python自然语言工具包(NLTK)。你需要安装库,如果你已经有它在你的电脑上,您还需要安装包需要的数据,如下:
import nltk nltk.download() # Download text data sets, including stop words |
现在我们可以使用nltk的停止词:
from nltk.corpus import stopwords # Import the stop word list print stopwords.words("english") |
这将允许您查看英文停止词的列表。从我们的电影评论里删除停顿词,做的事:
# Remove stop words from "words" words = [w for w in words if not w in stopwords.words("english")] print words |
这看起来每个单词在我们的“单词”列表,每个单词和丢弃任何停止词的列表。所有这些步骤之后,您的评论现在应该开始这样的:
[u'stuff', u'going', u'moment', u'mj', u've', u'started', u'listening', u'music', u'watching', u'odd', u'documentary', u'watched', u'wiz', u'watched', u'moonwalker', u'maybe', u'want', u'get', u'certain', u'insight', u'guy', u'thought', u'really', u'cool', u'eighties', u'maybe', u'make', u'mind', u'whether', u'guilty', u'innocent', u'moonwalker', u'part', u'biography', u'part', u'feature', u'film', u'remember', u'going', u'see', u'cinema', u'originally', u'released', u'subtle', u'messages', u'mj', u'feeling', u'towards', u'press', u'also', u'obvious', u'message', u'drugs', u'bad', u'm', u'kay',.....] |
不要担心每个单词前的“u”;它只是表明,Python是内部代表每个单词作为一个unicode字符串。
还有许多其他的事情我们可以做的数据——例如,词干提取和Lemmatizing(NLTK中可用)允许我们把“信息”,“消息”,和“消息”是同一个词,肯定是有用的。然而,为简单起见,本教程将停止在这里。
联合起来
现在我们有代码来清洁一个review,但我们需要清洁25000训练review!使我们的代码可重用,让我们创建一个函数,可以多次调用:
def review_to_words( raw_review ): # Function to convert a raw review to a string of words # The input is a single string (a raw movie review), and # the output is a single string (a preprocessed movie review) # # 1. Remove HTML review_text = BeautifulSoup(raw_review).get_text() # # 2. Remove non-letters letters_only = re.sub("[^a-zA-Z]", " ", review_text) # # 3. Convert to lower case, split into individual words words = letters_only.lower().split() # # 4. In Python, searching a set is much faster than searching # a list, so convert the stop words to a set stops = set(stopwords.words("english")) # # 5. Remove stop words meaningful_words = [w for w in words if not w in stops] # # 6. Join the words back into one string separated by space, # and return the result. return( " ".join( meaningful_words )) |
这里有两个新情况:首先,我们将停止单词列表转换为不同的数据类型,set。这是为速度;因为我们会调用这个函数数万次,它需要快,和搜索集在Python中搜索列表要快得多。
第二,我们加入了单词回一个段落。这是为了使输出更容易使用在我们词袋,如下。定义上述函数后,如果你为一个评论:调用该函数
clean_review = review_to_words( train["review"][0] ) print clean_review |
它应该给你相同的输出作为个人的所有步骤,我们在前面的教程部分。现在让我们遍历和清洁所有的训练集一次(这可能需要几分钟的时间取决于你的电脑):
# Get the number of reviews based on the dataframe column size num_reviews = train["review"].size # Initialize an empty list to hold the clean reviews clean_train_reviews = [] # Loop over each review; create an index i that goes from 0 to the length # of the movie review list for i in xrange( 0, num_reviews ): # Call our function for each one, and add the result to the list of # clean reviews clean_train_reviews.append( review_to_words( train["review"][i] ) ) |
有时它可能允许代码很长时间。它可以帮助编写代码,这样它给状态更新。Python打印一个状态更新后每1000次review更新一次,尝试添加一两行上面的代码:
print "Cleaning and parsing the training set movie reviews...\n" clean_train_reviews = [] for i in xrange( 0, num_reviews ): # If the index is evenly divisible by 1000, print a message if( (i+1)%1000 == 0 ): print "Review %d of %d\n" % ( i+1, num_reviews ) clean_train_reviews.append( review_to_words( train["review"][i] )) |
从词袋模型创建特征值Features(使用scikit-learn)
现在我们有我们的train回顾整理,我们如何将它们转换为某种数字表示法为机器学习?一个常见的方法称词袋模型。袋子里的单词模型学习词汇从所有的文档,然后每个文档模型通过计算每个单词出现的次数。例如,考虑以下两个句子:
Sentence 1: "The cat sat on the hat"
Sentence 2: "The dog ate the cat and the hat"
从这两句话,我们的词汇表如下:
{ the, cat, sat, on, hat, dog, ate, and }
获取词袋模型,我们统计每个单词出现的次数在每个句子。句子1,”the”现两次,“cat”,“sat”,“on”,和“hat”每出现一次,所以句子1的特征向量:
{ the, cat, sat, on, hat, dog, ate, and }
Sentence 1: { 2, 1, 1, 1, 1, 0, 0, 0 }
同样的,句子2的特征向量是: { 3, 1, 0, 0, 1, 1, 1, 1}
在IMDB数据中,我们有大量的评论,这将给我们一个大的词汇量。限制特征向量的大小,我们应该选择一些最大的词汇量大小。下面,我们使用最频繁的5000字(记住停止词已经被移除)。
我们将使用从scikit-learn feature_extraction模块来创建bag-of-words特性。如果你做了随机森林教程泰坦尼克号你应该已经安装了scikit-learn;否则,您将需要安装它。
print "Creating the bag of words...\n" from sklearn.feature_extraction.text import CountVectorizer # Initialize the "CountVectorizer" object, which is scikit-learn's # bag of words tool. vectorizer = CountVectorizer(analyzer = "word", \ tokenizer = None, \ preprocessor = None, \ stop_words = None, \ max_features = 5000) # fit_transform() does two functions: First, it fits the model # and learns the vocabulary; second, it transforms our training data # into feature vectors. The input to fit_transform should be a list of # strings. train_data_features = vectorizer.fit_transform(clean_train_reviews) # Numpy arrays are easy to work with, so convert the result to an # array train_data_features = train_data_features.toarray() |
看看现在的训练数据数组的样子:
>>> print train_data_features.shape (25000, 5000) |
它有25000行和5000特性(每个词汇的词)。
注意CountVectorizer附带自己的选项自动做预处理,标记,和停止词删除——这些,而不是指定“None”,我们可以使用一个内置的方法或指定使用我们自己的函数。看到函数文档以了解更多的细节。然而,我们想写自己的函数进行数据清洗在本教程向您展示它是如何一步一步完成。
现在袋模型训练,让我们看一下词汇:
# Take a look at the words in the vocabulary vocab = vectorizer.get_feature_names() print vocab |
如果你感兴趣,你也可以打印词汇表中的每个单词的数量:
import numpy as np # Sum up the counts of each vocabulary word dist = np.sum(train_data_features, axis=0) # For each, print the vocabulary word and the number of times it # appears in the training set for tag, count in zip(vocab, dist): print count, tag |
随机森林
在这一点上,我们有数字train功能包的单词和原始情绪标签为每个特征向量,让我们来做一些监督学习!在这里,我们用随机森林分类器,我们介绍了泰坦尼克号教程。随机森林算法包含在scikit-learn(随机森林使用很多树分类器进行预测,因此,“森林”)。下面,我们将树的数量设置为100作为一个合理的默认值。更多的树可能(也可能不)表现得更好,但肯定会需要更长的时间。同样,更多的功能包括对于每个审查,这需要的时间越长。
print "Training the random forest..." from sklearn.ensemble import RandomForestClassifier # Initialize a Random Forest classifier with 100 trees forest = RandomForestClassifier(n_estimators = 100) # Fit the forest to the training set, using the bag of words as # features and the sentiment labels as the response variable # # This may take a few minutes to run forest = forest.fit( train_data_features, train["sentiment"] ) |
创建一个提交
剩下的工作就是运行训练随机森林对我们的测试集和创建一个提交文件。如果您还没有这样做。从数据页下载testData.tsv。这个文件包含25000个评论和id,我们的任务是预测sentiment标签。
注意,当我们使用测试集袋的话,我们只叫“transform”,不是“fit_transform”作为我们的训练集,在机器学习,你不应该使用测试设置为适合您的模型,否则你可能过度拟合。出于这个原因,我们保持测试设置禁区,直到我们准备作出预测。
# Read the test data test = pd.read_csv("testData.tsv", header=0, delimiter="\t", \ quoting=3 ) # Verify that there are 25,000 rows and 2 columns print test.shape # Create an empty list and append the clean reviews one by one num_reviews = len(test["review"]) clean_test_reviews = [] print "Cleaning and parsing the test set movie reviews...\n" for i in xrange(0,num_reviews): if( (i+1) % 1000 == 0 ): print "Review %d of %d\n" % (i+1, num_reviews) clean_review = review_to_words( test["review"][i] ) clean_test_reviews.append( clean_review ) # Get a bag of words for the test set, and convert to a numpy array test_data_features = vectorizer.transform(clean_test_reviews) test_data_features = test_data_features.toarray() # Use the random forest to make sentiment label predictions result = forest.predict(test_data_features) # Copy the results to a pandas dataframe with an "id" column and # a "sentiment" column output = pd.DataFrame( data={"id":test["id"], "sentiment":result} ) # Use pandas to write the comma-separated output file output.to_csv( "Bag_of_Words_model.csv", index=False, quoting=3 ) |
祝贺你,你已经准备好让你的第一个提交!尝试不同的东西,看看你的变化结果。清洁不同的评论,可以选择一个不同的数字单词的袋的话表示,波特阻止,不同的分类器,或任何其他的东西。尝试你的NLP排在一个不同的数据集,您也可以去我们的Rotten Tomatoes比赛。或者,如果你准备一些完全不同的,沿深度学习和词向量页面。
推荐阅读