当前位置:   article > 正文

尝试用bert做文本聚类_基于bert的文本聚类

基于bert的文本聚类

                                               尝试用bert做文本聚类

以前文本聚类多以TF-IDF构建词权重的方法进行,在本文中尝试用bert提取的向量做文本聚类。对于bert模型,尝试提取不同层的特征,尝试对bert做fun-tune,观察相应特征对应的文本聚类的效果

数据

数据使用的是百度2020语言比赛的数据,该数据是标注并分类好的,所以在聚类的情况下,省去了聚类时对k值的搜索,同时可以可以根据标注好的数据和聚类得到的数据比较,从侧面评价聚类的效果

下载地址

https://pan.baidu.com/s/1hIiGutDm73vo7lw31H-tfw  提取码 4gqn

 

工具和环境

python3 ,bert4keras 0.7.5

强烈推荐苏剑林大佬的bert4keras https://github.com/bojone/bert4keras

 

使用bert提取特征

 

  1. from bert4keras.backend import keras
  2. from bert4keras.models import build_transformer_model
  3. from bert4keras.tokenizers import Tokenizer
  4. import numpy as np
  5. config_path = '/root/kg/bert/chinese_L-12_H-768_A-12/bert_config.json'
  6. checkpoint_path = '/root/kg/bert/chinese_L-12_H-768_A-12/bert_model.ckpt'
  7. dict_path = '/root/kg/bert/chinese_L-12_H-768_A-12/vocab.txt'
  8. tokenizer = Tokenizer(dict_path, do_lower_case=True) # 建立分词器
  9. model = build_transformer_model(config_path, checkpoint_path) # 建立模型,加载权重
  10. # 编码测试
  11. token_ids, segment_ids = tokenizer.encode(u'语言模型')
  12. print('\n ===== predicting =====\n')
  13. print(model.predict([np.array([token_ids]), np.array([segment_ids])]))
  14. """
  15. 输出:
  16. [[[-0.63251007 0.2030236 0.07936534 ... 0.49122632 -0.20493352
  17. 0.2575253 ]
  18. [-0.7588351 0.09651865 1.0718756 ... -0.6109694 0.04312154
  19. 0.03881441]
  20. [ 0.5477043 -0.792117 0.44435206 ... 0.42449304 0.41105673
  21. 0.08222899]
  22. [-0.2924238 0.6052722 0.49968526 ... 0.8604137 -0.6533166
  23. 0.5369075 ]
  24. [-0.7473459 0.49431565 0.7185162 ... 0.3848612 -0.74090636
  25. 0.39056838]
  26. [-0.8741375 -0.21650358 1.338839 ... 0.5816864 -0.4373226
  27. 0.56181806]]]
  28. """

这段代码是bert4keras的示例代码,可以将 '语言模型' 这句话的特征提取出来,本文中采用下面两种方式作表征句向量

1  取特征第一个位置的向量作为句向量,即 model.predict([np.array([token_ids]), np.array([segment_ids])])[0][0]

2  对所有特征取平均作为句向量

处理数据,得到数据集对应的词向量

  1. #! -*- coding: utf-8 -*-
  2. # 测试代码可用性: 提取特征
  3. from bert4keras.backend import keras
  4. from bert4keras.models import build_transformer_model
  5. from bert4keras.tokenizers import Tokenizer
  6. import numpy as np
  7. import json
  8. from keras.models import Model
  9. from bert4keras.backend import keras, K
  10. from bert4keras.models import build_transformer_model
  11. from bert4keras.tokenizers import Tokenizer
  12. from bert4keras.optimizers import Adam
  13. from bert4keras.snippets import sequence_padding, DataGenerator
  14. from bert4keras.snippets import open
  15. from bert4keras.layers import ConditionalRandomField
  16. from keras.layers import Dense
  17. from keras.models import Model
  18. from tqdm import tqdm
  19. from keras.layers import Dropout, Dense
  20. from keras_bert import extract_embeddings
  21. # 如果需要禁止GPU的话可以使用下面的环境变量 对于不同的系统可能为-1 或者 0
  22. # os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
  23. config_path = 'D:/model/chinese_L-12_H-768_A-12/bert_config.json'
  24. checkpoint_path = 'D:/model/chinese_L-12_H-768_A-12/bert_model.ckpt'
  25. dict_path = 'D:/model/chinese_L-12_H-768_A-12/vocab.txt'
  26. def load_data(filename):
  27. D = []
  28. with open(filename,encoding='utf-8') as f:
  29. for l in f:
  30. l = json.loads(l)
  31. D.append(l['text'])
  32. return D
  33. if __name__ == "__main__":
  34. # 词向量获取方法 cls,mean,
  35. vector_name = 'cls'
  36. tokenizer = Tokenizer(dict_path, do_lower_case=True) # 建立分词器
  37. model = build_transformer_model(config_path, checkpoint_path) # 建立模型,加载权重
  38. maxlen = 70
  39. # 读取处理数据
  40. f1 = 'D:/cluster/data/train.json'
  41. res = load_data(f1)
  42. output = []
  43. print('开始提取')
  44. # 根据提取特征的方法获得词向量
  45. for r in res:
  46. token_ids, segment_ids = tokenizer.encode(r,max_length=maxlen)
  47. if vector_name == 'cls':
  48. cls_vector = model.predict([np.array([token_ids]), np.array([segment_ids])])[0][0]
  49. output.append(cls_vector)
  50. elif vector_name == 'mean':
  51. new = []
  52. vector = model.predict([np.array([token_ids]), np.array([segment_ids])])[0]
  53. for i in range(768):
  54. temp = 0
  55. for j in range(len(vector)):
  56. temp += vector[j][i]
  57. new.append(temp/(len(vector)))
  58. output.append(new)
  59. print('保存数据')
  60. np.savetxt("text_vectors.txt",output)

如果设置提取方式为cls 取特征第一个位置的向量作为句向量 设置为mean 对所有特征取平均作为句向量 最后会将结果保存到txt文件中

对数据聚类

得到了我们想要的数据,我们就可以对他们聚类了,这里使用sklearn,分别用kmeans 和Birch聚类

  1. from sklearn.cluster import KMeans
  2. from sklearn.cluster import Birch
  3. feature = np.loadtxt("text_vectors.txt")
  4. clf = KMeans(n_clusters=9)
  5. s = clf.fit(feature)
  6. kn_pre = clf.predict(feature)
  7. birch_pre = Birch(branching_factor=10, n_clusters = 9, threshold=0.5,compute_labels=True).fit_predict(feature

聚类评估

由于我们使用的数据是已经标注好的,我们可以对比聚类后的数据和原来标注数据,类似于从上帝视角去观察聚类的结果,当然也可以使用一些评价聚类效果的参数,比如calinski_harabaz_score

同时为了更深入的测试bert的聚类效果,尝试提取了bert倒数第2层,倒数第3层的特征。并且用了10%的语料进行了简单的fun-tune,即简单的分类微调,提取了fun-tune后的bert的特征 一起进行评估,结果如下

 k-meansBirch
bert-cls0.380.32
bert-mean 0.520.41
bert倒数第二层cls0.470.42
bert倒数第二层mean0.520.41
bert- fun-tune-mean0.930.93
bert- fun-tune倒数第二层mean0.910.91

图中分数为依据标注数据计算得到为聚类准确率,分数越大,表示模型聚类效果越好。   

可以看到在这个任务中bert进行微调后得到的句向量求平均得到的特征,可以非常好的聚类,但是没有fun-tune的bert 特征聚类的效果一般,并不能直接使用

 

一些思考

为什么bert-fun-tune后的特征会表现的这么好?

我们在用fun-tune之后 虽然fun-tune语料很小,但此时它也是一个能力非常强的分类模型,即使不用聚类的方法 直接让它对文本分类可能结果就非常的好。这时再做聚类似乎多此一举。但是我认为这个实验还是有意义的。依据著名的聚类假设:同类的文档相似度较大,而不同类的文档相似度较小。假如我们拿到一批大量的没有标注的数据,我们认为这个数据是可分类的,我们可以先观察它的小部分样本,简单按照我们期望的方向进行分类,分成比如 ['财经','产品行为','交往','其他'],用这个足够小的样本微调,再进行聚类,可能获得更好聚类效果。

是否bert的最后一层的效果是最好的?

最近看了一篇关于bert的论文 How to Fine-Tune Bert for Text Classification 其中有讨论了使用bert不同层 的分类效果,所以也尝试了一下。对于不同的语料不同的任务,这个问题可能有不同的答案。总体来说不同层的效果出入不大,大家可以都尝试一下。

 

Github

最后附上项目代码 https://github.com/hgliyuhao/cluster

微调后的bert权重

https://pan.baidu.com/s/1TPIQBUPcsCDMvPXmx8d9sg 提取码 9f63

欢迎大家交流

 

 

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/425457
推荐阅读
相关标签
  

闽ICP备14008679号