110-文本分析之文本特征提取

参考:《文本数据挖掘——基于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")
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容