参考:《文本数据挖掘——基于R语言》
1、基本特征提取
基本特征包括:字符的数量、句子的数量、每个词的长度,标点符号的数量等。
# 只能分析英文
p_load(textfeatures)
txt <- "1000pcs 8*32mm 0.5ml Plastic Centrifuge Tube Test Tubing Vial Clear Plastic Container Home Garden Storage Bottles。1000pcs 6*22mm 0.2ml Plastic Bottles Gardening Storage Container Transparent Plastic Vials PCR Centrifuge Tube"
# sentiment参数能够自动进行情感分析,word_dims则可以使用词袋模型对文本进行向量化,normalize参数可以对数据按列进行归一化。全部关闭
textfeatures(txt, sentiment = F, word_dims = F, normalize = F,
verbose = F) %>%
# 显示所有结果
print(width = Inf)
## # A tibble: 1 × 29
## n_urls n_uq_urls n_hashtags n_uq_hashtags n_mentions n_uq_mentions n_chars
## <int> <int> <int> <int> <int> <int> <int>
## 1 0 0 0 0 0 0 196
## n_uq_chars n_commas n_digits n_exclaims n_extraspaces n_lowers n_lowersp
## <int> <int> <int> <int> <int> <int> <dbl>
## 1 35 0 18 0 0 147 0.751
## n_periods n_words n_uq_words n_caps n_nonasciis n_puncts n_capsp
## <int> <int> <int> <int> <int> <int> <dbl>
## 1 3 29 20 26 0 2 0.137
## n_charsperword n_first_person n_first_personp n_second_person
## <dbl> <int> <int> <int>
## 1 6.57 0 0 0
## n_second_personp n_third_person n_tobe n_prepositions
## <int> <int> <int> <int>
## 1 0 0 0 0
● n_urls:文本中包含的URL的数量。
● n_uq_urls:文本中包含唯一URL的数量(本例中的计算结果并不准确)。
● n_chars:总字符数量;
● n_commas:逗号的数量;
● n_lowers:小写字符数量;
● n_lowersp:小写字符比例;
● n_words:单词总数量;
● n_uq_words:唯一单词的数量;
● n_first_person:第一人称单数单词的数量;
● n_second_personp:第二人称复数单词的数量;
● n_prepositions:介词的数量。
2、基于TF-IDF的特征提取
TF-IDF就是词频TF与逆文档频率IDF的乘积,它背后的思想是:词语的重要性与它在文件中出现的次数成正比,但同时会随着它在语料库中出现的频率成反比。
library(pacman)
p_load(dplyr, stringr, purrr)
2.1 读取数据
随便文本代替即可,包括两列,一列为文档名或编号,一列为文本内容。
storagebottles <- read.csv("dataset/ali/storagebottles0905.csv",
header = F) %>%
set_names(c("sku_name", "sku_price", "sku_sale_volume", "sku_score",
"sku_ship", "sku_isNewin", "sku_isPromotion",
"sku_isTopselling", "shop_name", "sku_link", "category4")) %>%
distinct(.keep_all = T)
storagebottles <- storagebottles %>%
filter(!is.na(sku_name)) %>%
filter(str_detect(sku_price, "^US")) %>%
filter(str_detect(sku_link, "aliexpress")) %>%
filter(str_detect(sku_sale_volume, "sold")) %>%
mutate(category = "home",
category2 = "Home Storage",
category3 = "Storage Bottles & Jars") %>%
mutate(sku_id = str_extract(sku_link, "\\d{16}"),
sku_link = paste0("http:", sku_link)) %>%
mutate(sku_id = as.character(sku_id)) %>%
arrange(sku_sale_volume) %>%
group_by(sku_id, .drop = T) %>%
slice_tail(n=1) %>%
ungroup()
df <- select(storagebottles, sku_id, sku_name)
count(df, sku_id, stem) %>%
bind_tf_idf(term = stem,
document = sku_id,
n = n)
## # A tibble: 19,586 × 6
## sku_id stem n tf idf tf_idf
## <chr> <chr> <int> <dbl> <dbl> <dbl>
## 1 2251801564728378 0.5ml 1 0.0588 5.70 0.335
## 2 2251801564728378 1000pcs 1 0.0588 6.40 0.376
## 3 2251801564728378 32mm 1 0.0588 7.09 0.417
## 4 2251801564728378 8 1 0.0588 4.89 0.288
## 5 2251801564728378 bottl 1 0.0588 0.542 0.0319
## 6 2251801564728378 centrifug 1 0.0588 4.79 0.282
## 7 2251801564728378 clear 1 0.0588 2.29 0.135
## 8 2251801564728378 contain 1 0.0588 0.549 0.0323
## 9 2251801564728378 garden 1 0.0588 5.48 0.322
## 10 2251801564728378 home 1 0.0588 2.60 0.153
## # … with 19,576 more rows
2.2 词嵌入
2.2.1 基于BOW(词袋模型)
这种方法是信息的简化表示,把文本表示为词语的集合,不考虑文中的语法或者词序,但是能够对内容的多样性进行记录。
可以使用词频或者使用TF-IDF进行表征:
count(df, sku_id, stem) %>%
bind_tf_idf(term = stem,
document = sku_id,
n = n) %>%
cast_dfm(document = sku_id,
term = stem,
value = tf_idf)
## Document-feature matrix of: 1,198 documents, 1,641 features (99.00% sparse) and 0 docvars.
## features
## docs 0.5ml 1000pcs 32mm 8 bottl
## 2251801564728378 0.3354185 0.3761919 0.4169652 0.2877167 0.03186020
## 2251801564729229 0 0.4263508 0 0 0.03610822
## 2251832228713647 0 0 0 0 0.02850649
## 2251832295192632 0 0 0 0 0
## 2251832346856028 0 0 0 0 0
## 2251832357989488 0 0 0 0 0
## features
## docs centrifug clear contain garden home
## 2251801564728378 0.2815190 0.1348599 0.03228370 0.3222924 0.1529278
## 2251801564729229 0.3190549 0 0.03658819 0.3652647 0
## 2251832228713647 0 0 0 0 0
## 2251832295192632 0 0 0 0 0
## 2251832346856028 0 0 0.03430143 0 0
## 2251832357989488 0 0 0.05226884 0 0
## [ reached max_ndoc ... 1,192 more documents, reached max_nfeat ... 1,631 more features ]
text2vec包对词袋模型的高性能运算提供了强大的实现方法:
p_load(text2vec)
df <- select(storagebottles, sku_id, sku_name)
# 预处理及分词
it <- itoken(df$sku_name,
# 预处理函数定义,转换为小写
preprocessor = tolower,
# 分词器定义,使用空格分割
tokenizer = word_tokenizer,
ids = df$sku_id,
# 是否显示进度条
progressbar = F)
# 构建词汇表
vocab <- create_vocabulary(it)
# 文本向量化
vec <- vocab_vectorizer(vocab)
# 创建DTM矩阵,顺便测试时间
system.time({
dtm_train <- create_dtm(it = it,
vectorizer = vec)
})
## 用户 系统 流逝
## 0.00 0.03 0.03
2.2.2 基于word2vec
word2vec是一组用于生成词向量的自然语言处理工具,主要是基于双层神经网络,经过训练后可以为单词生成一个向量空间,为每一个单词都分配一个向量。在生成的向量空间中,意思越相近的单词向量之间的距离越小,反之则越大。word2vec有两种模式,分别是CBOW和skip-gram。
p_load(word2vec)
mod <- word2vec(x = df$sku_name,
# 输出向量的维度
dim = 10,
# 迭代次数
iter = 20,
# 使用COBW模型还是skip-gram模型
type = "cbow")
# 转换为矩阵
emb <- as.matrix(mod)
head(emb)
## [,1] [,2] [,3] [,4] [,5] [,6]
## Beads -1.9400728 -0.0888407 -0.08349484 -1.2562962 -0.2697616 -1.8421835
## Weekly -1.1608911 -0.4651599 -0.25436968 -1.4303011 -0.9386142 -1.7998306
## PVC 0.3128605 0.8036703 0.09885665 -2.0803137 0.7472407 -0.4748393
## Foaming -0.7814559 -0.2095719 0.88936573 0.2705340 -0.1661042 -1.4170694
## Flat 0.4067485 -0.7098307 -0.97712469 -0.8143641 0.9589156 -0.3076381
## Herbs -0.7869626 1.7579030 0.37374184 -1.6308879 -1.1516812 -0.4863058
## [,7] [,8] [,9] [,10]
## Beads -0.5022707 0.6688779 0.68726629 0.06747537
## Weekly -0.5683426 1.3080628 0.29630077 0.28856692
## PVC -1.0187222 1.5673116 0.50875384 0.61791813
## Foaming -2.1313248 0.8421388 -1.07562840 0.19196655
## Flat 0.2689168 1.7321179 0.43238384 1.85447288
## Herbs -0.1345760 -0.6410958 0.05552098 1.22308218
# 与container最接近的5个词
predict(mod, c("plastic"), type = "nearest", top_n = 5)
## $plastic
## term1 term2 similarity rank
## 1 plastic transparent 0.9843451 1
## 2 plastic empty 0.9740745 2
## 3 plastic small 0.9626620 3
## 4 plastic perfume 0.9494769 4
## 5 plastic bottle 0.9455841 5
# 模型保存,path为自定义路径
write.word2vec(mod, file = path)
# 模型读取
read.word2vec(path)
2.2.3 基于Glove
它是一种用于获取词向量表示的无监督学习算法,与BOW相似,都是基于词之间共现关系,但是这个算法能够保留基于上下文关系保留更多的语境信息,能够取得更好的向量化效果。
p_load(text2vec)
tokens <- df %>%
# 使用空格分词
mutate(sku_name = tolower(sku_name)) %>%
mutate(token = space_tokenizer(sku_name)) %>%
pull(token)
# 设置迭代器
it <- itoken(df$sku_name, # 这个是语料
# 转为小写
preprocessor = tolower,
# 使用空格分词
tokenizer = space_tokenizer,
ids = df$sku_id,
progressbar = F)
# 创建词汇表,加载停止词
vocab <- create_vocabulary(it, stopwords = tm::stopwords())
# 保留出现5次以上的词
vocab <- prune_vocabulary(vocabulary = vocab,
term_count_min = 5L)
# 形成语料文件
vectorizer <- vocab_vectorizer(vocabulary = vocab)
# 构建DTM矩阵,窗口宽度设置为5
tcm <- create_tcm(it, vectorizer, skip_grams_window = 5L)
# 设置词向量维度设为50
glove <- GlobalVectors$new(rank = 50,
# 权重为最大共现数
x_max = 10)
# SDG迭代次数为10
wv_main <- glove$fit_transform(x = tcm, n_iter = 10,
convergence_tol = 0.01,
# 不加参数默认所有线程并行计算
n_threads = 4)
## INFO [15:27:30.119] epoch 1, loss 0.3156
## INFO [15:27:30.137] epoch 2, loss 0.1382
## INFO [15:27:30.155] epoch 3, loss 0.1033
## INFO [15:27:30.172] epoch 4, loss 0.0841
## INFO [15:27:30.190] epoch 5, loss 0.0708
## INFO [15:27:30.208] epoch 6, loss 0.0610
## INFO [15:27:30.226] epoch 7, loss 0.0534
## INFO [15:27:30.243] epoch 8, loss 0.0473
## INFO [15:27:30.260] epoch 9, loss 0.0423
## INFO [15:27:30.277] epoch 10, loss 0.0382
wv_content <- glove$components
dim(wv_content)
## [1] 50 538
# 最终结果
word_vectors <- wv_main + t(wv_content)
2.2.4 基于fastText
可以完成词嵌入和文本分类等任务。与GloVe类似,它也是word2vec的一种扩展,但它利用了神经网络对词语进行向量化,能够对字符级的特征进行学习。
fastText 模型架构和 Word2Vec 中的 CBOW 模型很类似。不同之处在于,fastText 预测标签,而 CBOW 模型预测中间词。
举例来说:fastText能够学会“男孩”、“女孩”、“男人”、“女人”指代的是特定的性别,并且能够将这些数值存在相关文档中。然后,当某个程序在提出一个用户请求(假设是“我女友现在在儿?”),它能够马上在fastText生成的文档中进行查找并且理解用户想要问的是有关女性的问题。
p_load(text2vec)
# 一直报错,原因是R4.2.1,需要安装rtools4.2,否则安装会报错
p_load_gh("pommedeterresautee/fastrtext")
# 生成文本
txt <- df %>%
pull(sku_name) %>%
tolower() %>%
str_remove_all("[:punct:]")
# 构建文本文档,执行向量化
tmp_file_txt <- tempfile()
tmp_file_model <- tempfile()
writeLines(txt, con = tmp_file_txt)
execute(commands = c("skipgram", "-input", tmp_file_txt,
"-output", tmp_file_model,
"-verbose", 1))
##
Read 0M words
## Number of words: 548
## Number of labels: 0
##
Progress: 100.0% words/sec/thread: 34165 lr: 0.000000 avg.loss: 3.049489 ETA: 0h 0m 0s
# 载入模型
model <- load_model(tmp_file_model)
# 获取字典
dict <- get_dictionary(model)
# 获得词向量
word_vectors <- get_word_vectors(model)
# 释放内存
unlink(tmp_file_txt)
unlink(tmp_file_model)
rm(model)
gc()
## used (Mb) gc trigger (Mb) max used (Mb)
## Ncells 3840040 205.1 6683084 357.0 6683084 357.0
## Vcells 7261552 55.5 14786712 112.9 10146316 77.5
execute()可以输入的函数:
以下参数是强制性的:
-input 训练文件路径
-output 输出文件路径
以下参数是可选的:
-verbose 详细级别,默认2
字典选项:
-minCount 最少单词出现次数,默认5
-minCountLabel 最少标签出现次数,默认0
-wordNgrams ngram最大单词数,默认1
-bucket 存储体数量,默认2000000
-minn ngram最小字符长度,默认3
-maxn ngram最大字符长度,默认6
-t 采样阈值,默认0.0001
-label 标签前缀,默认_label_
以下训练参数是可选的:
-lr 学习速度,默认0.05
-lrUpdateRate 学习速度的更新率,默认100
-dim 词矩阵维度,默认100
-ws 上下文窗口大小,默认5
-epoch 时期数,默认5
-neg 负采样数,默认5
-loss 损失函数 {ns, hs, softmax},默认ns
-thread 线程数,默认12
-pretrainedVectors 用于监督的预训练词向量,默认为空
-saveOutput 是否保存输出参数,默认0
以下量化参数可选:
-cutoff 要保留的单词和 ngram 的数量,默认0
-retrain 如果应用了截止,则微调嵌入,默认0
-qnorm 分别量化范数,默认0
-qout 量化分类器,默认0
-dsub 每个子向量的大小,默认2
3、文档向量化
对文档进行分词,然后利用获得的词向量,用文档中所有词汇向量进行加和,然后再除以一个标量来获得文档表示向量。
p_load(textTinyR, text2vec)
tokens <- df %>%
mutate(sku_name = tolower(sku_name)) %>%
mutate(token = space_tokenizer(sku_name)) %>%
pull(token)
it <- itoken(tokens, progressbar = F)
vocab <- create_vocabulary(it)
vocab <- prune_vocabulary(vocab, term_count_min = 5L)
vectorizer <- vocab_vectorizer(vocab)
tcm <- create_tcm(it, vectorizer, skip_grams_window = 5L)
glove <- GlobalVectors$new(rank = 50, x_max = 10)
# SDG迭代次数为10
wv_main <- glove$fit_transform(x = tcm, n_iter = 10)
## INFO [15:27:41.400] epoch 1, loss 0.3118
## INFO [15:27:41.417] epoch 2, loss 0.1380
## INFO [15:27:41.433] epoch 3, loss 0.1033
## INFO [15:27:41.450] epoch 4, loss 0.0842
## INFO [15:27:41.470] epoch 5, loss 0.0710
## INFO [15:27:41.486] epoch 6, loss 0.0613
## INFO [15:27:41.504] epoch 7, loss 0.0537
## INFO [15:27:41.520] epoch 8, loss 0.0476
## INFO [15:27:41.537] epoch 9, loss 0.0427
## INFO [15:27:41.553] epoch 10, loss 0.0385
wv_content <- glove$components
dim(wv_content)
## [1] 50 548
# 最终结果
word_vectors <- wv_main + t(wv_content)
# 保存词向量
write.table(word_vectors, file = "wv.txt", col.names = F)
# 转化清洗
readLines("wv.txt") %>%
str_remove_all("\\\"") %>%
writeLines("wv.txt")
# 提取需要向量化的文档
tok_text <- tokens
# 文档向量化
init <- Doc2Vec$new(token_list = tok_text,
word_vector_FILE = "wv.txt")
# method参数使用了“sum_sqrt”,它表示文档向量会对词汇向量先进行简单加和获得一个新的向量
# INITIAL_WORD_VECTOR,然后对这个向量求平方和再开方得到一个标量k,
# 最后INITIAL_WORD_VECTOR除以k就是最后的文档向量。
out <- init$doc2vec_methods(method = "sum_sqrt")
# 删除向量化词汇表
unlink("wv.txt")