??? 数据准备上,如果你自己有已经清洗好的数据当然更好,得是CSV格式的,没有的话,我们暂时使用TMT自带的测试数据集pubmed-oa-subset.csv。另外,下载下来的数据也好,你自己的数据也好,请保证编码格式为utf8.另外需要注意的是,下载下来的数据并不能直接用于分析,原因在于该数据集本来是在unix系统下测试的,现在我们是在windows系统下。因此,一旦运行,必然报错。解决这个问题的最简单的方法就是,另存一个CSV文件。如果想要进一步的操作,推荐一个工具:Notepad++。这是修改任何语言代码的利器,也是我们下面要用到的,毕竟txt格式下,观感并不好。
??? 好了,到此,准备工作基本上就结束了。
??? 2、文本预处理
??? 预处理的工具非常多,特别是英文文本,这里我们仅介绍TMT自带的预处理方法。
??? 你也可以下载TMT自带的案例代码文件example-1-dataset.scala.,以下是源代码:
val source = CSVFile("pubmed-oa-subset.csv") ~> IDColumn(1);
val tokenizer = {
SimpleEnglishTokenizer() ~> // tokenize on space and punctuation
CaseFolder() ~> // lowercase everything
WordsAndNumbersOnlyFilter() ~> // ignore non-words and non-numbers
MinimumLengthFilter(3) // take terms with >=3 characters
}
val text = {
source ~> // read from the source file
Column(4) ~> // select column containing text
TokenizeWith(tokenizer) ~> // tokenize with tokenizer above
TermCounter() ~> // collect counts (needed below)
TermMinimumDocumentCountFilter(4) ~> // filter terms in <4 docs
TermDynamicStopListFilter(30) ~> // filter out 30 most common terms
DocumentMinimumLengthFilter(5) // take only docs with >=5 terms
}
??? 以上代码均是可以修改的。如果你的代码中含有IDColumn(1),那么TMT会将你csv数据的第一列作为ID号,ID是LDA模型中识别每一条文档是何种主题的唯一识别标志,所以非常重要。如果你的数据中并没有ID号,那么请移除代码IDColumn(1),TMT会自动地将行号作为ID号。如果你的数据第一行包含字段名,你可以通过以下代码移除第一行:
val source = CSVFile("your-csv-file.csv") ~> IDColumn(yourIdColumn) ~> Drop(1);
预处理第一步:分词
??? TMT仅提供了两个分词器,即默认的SimpleEnglishTokenizer()和可选的WhitespaceTokenizer().差别在于,如果你的数据在这之前没有做任何处理,那么请选用SimpleEnglishTokenizer()及其后续步骤;如果你对文本已然分词,那么请选用WhitespaceTokenizer()及视情况选用后续步骤;中文分词后,就只能选用WhitespaceTokenizer()。我们现在先以默认的SimpleEnglishTokenizer()为例。当然了,TMT还提供正则表达式分词器,但在此意义不大。
预处理第二步:过滤(抽取有意义的词)
??? 后续步骤基本上都是这种公用。默认代码中CaseFolder()是将所有字母转化为小写字母;WordsAndNumbersOnlyFilter
()是仅取数字和单词,单词是基于字典的。因此一些奇怪的字母组合会被过滤掉;MinimumLengthFilter
(3)的意思是过滤掉小于3的单词,记住这里是小于3,而不是小于等于3,因此三个字母的单词仍然被保留,但其实英语中三个字母的单词,如the,and,其实也是无意义的,所以这里可以设置为“4”.
??? 除了这些默认的参数,我们还可以选用以下参数:
PorterStemmer() //将具有相同词根的词合并,比如“book”和“books”,"have"和"had"
StopWordFilter("en") //过滤掉英文文本中止停词
??? 3、选定文本
??? 包括的默认代码有:
val text = {
source ~> // read from the source file
Column(4) ~> // select column containing text
TokenizeWith(tokenizer) ~> // tokenize with tokenizer above
TermCounter() ~> // collect counts (needed below)
TermMinimumDocumentCountFilter(4) ~> // filter terms in <4 docs
TermDynamicStopListFilter(30) ~> // filter out 30 most common terms
DocumentMinimumLengthFilter(5) // take only docs with >=5 terms
}
??? 其实就是上文代码的后半本分。由于CSV文件中可能有很多列以及很多意外情况(比如空单元格),因此需要选择有意义的文档。文本的预处理是针对字符,而这部分是针对文档。
??? 这里我们先取默认代码。
??? 4、训练模型
??? 默认代码如下:
// turn the text into a dataset ready to be used with LDA
val dataset = LDADataset(text);
// define the model parameters
val params = LDAModelParams(numTopics = 30,dataset = dataset,topicSmoothing = 0.01,termSmoothing = 0.01);
// Name of the output model folder to generate
val modelPath = file("lda-"+dataset.signature+"-"+params.signature);
// Trains the model: the model (and intermediate models) are written to the
// output folder. If a partially trained model with the same dataset and
// parameters exists in that folder,training will be resumed.
TrainCVB0LDA(params,dataset,output=modelPath,maxIterations=1000);
??? 这里默认就好,关键是看结果。
??? 经过1000次迭代,跑出的结果文件有很多,都保存在同文件夹下的名为"lda-"+dataset.signature+"-"+params.signature的子文件夹。
??? 结果文件很多,其中最为重要的是summary.txt。这就是该CSV文件中的文本所凝聚的主题,我们选定的默认主题凝聚数为30,分别为topic00—topic29,每个主题会有一个总权重,每个主题对应多个词,词后面跟着该词对该主题的贡献度。其实,这样看来,LDA模型与因子分析有些像,只不过因子分析是数字的公因子,而LDA主题是文本的公因子。
??? 这之后就是解释的问题,以及有监督或半监督下的LDA模型的问题,我们将逐步深入。