├── README.md
├── classifier_multi_label
├── README.md
├── __init__.py
├── albert_small_zh_google
│ ├── albert_config.json
│ └── vocab_chinese.txt
├── classifier_utils.py
├── data
│ ├── test.csv
│ ├── test_onehot.csv
│ ├── train.csv
│ ├── train_onehot.csv
│ └── vocabulary_label.txt
├── eval.py
├── hyperparameters.py
├── image
│ └── graph.png
├── lamb_optimizer.py
├── logdir
│ └── model_01
│ │ └── events.out.tfevents.1595940723.DESKTOP-QC1A83I
├── model
│ ├── model_load
│ │ └── README.md
│ └── model_save
│ │ └── READEME.md
├── modeling.py
├── modules.py
├── networks.py
├── optimization.py
├── predict.py
├── tokenization.py
├── train.py
└── utils.py
├── classifier_multi_label_denses
├── README.md
├── __init__.py
├── albert_small_zh_google
│ └── albert_config.json
├── classifier_utils.py
├── data
│ ├── test.csv
│ ├── test_onehot.csv
│ ├── train.csv
│ ├── train_onehot.csv
│ └── vocabulary_label.txt
├── eval.py
├── hyperparameters.py
├── lamb_optimizer.py
├── logdir
│ └── CML_Denses
│ │ └── events.out.tfevents.1602241580.DESKTOP-QC1A83I
├── model
│ ├── model_load
│ │ └── README.md
│ └── model_save
│ │ └── README.md
├── modeling.py
├── networks.py
├── optimization.py
├── predict.py
├── tokenization.py
├── train.py
└── utils.py
├── classifier_multi_label_seq2seq
├── README.md
├── __pycache__
│ ├── classifier_utils.cpython-37.pyc
│ ├── hyperparameters.cpython-37.pyc
│ ├── lamb_optimizer.cpython-37.pyc
│ ├── load.cpython-37.pyc
│ ├── modeling.cpython-37.pyc
│ ├── modules.cpython-37.pyc
│ ├── networks.cpython-37.pyc
│ ├── optimization.cpython-37.pyc
│ ├── predict.cpython-37.pyc
│ ├── tokenization.cpython-37.pyc
│ └── utils.cpython-37.pyc
├── albert_small_zh_google
│ ├── checkpoint
│ └── vocab_chinese.txt
├── classifier_utils.py
├── data
│ ├── test.csv
│ ├── train.csv
│ └── vocabulary_label.txt
├── eval.py
├── hyperparameters.py
├── lamb_optimizer.py
├── load.py
├── logdir
│ └── CML_Seq2Seq
│ │ └── Readme.txt
├── model
│ ├── model_load
│ │ └── README.md
│ └── model_save
│ │ └── README.md
├── modeling.py
├── modules.py
├── networks.py
├── optimization.py
├── predict.py
├── tokenization.py
├── train.py
└── utils.py
├── classifier_multi_label_textcnn
├── README.md
├── __init__.py
├── albert_small_zh_google
│ └── albert_config.json
├── classifier_utils.py
├── data
│ ├── test.csv
│ ├── test_onehot.csv
│ ├── train.csv
│ ├── train_onehot.csv
│ └── vocabulary_label.txt
├── eval.py
├── hyperparameters.py
├── image
│ ├── graph.png
│ └── loss.png
├── lamb_optimizer.py
├── model
│ ├── model_load
│ │ └── README.md
│ └── model_save
│ │ └── README.md
├── modeling.py
├── modules.py
├── networks.py
├── optimization.py
├── predict.py
├── tokenization.py
├── train.py
└── utils.py
├── evaluation
├── README.md
└── eval_multi_label.py
└── imgs
├── 01.png
├── 01b.png
├── 02.png
├── 09.png
├── 09b.jpg
├── 10.png
├── HELLONLP.png
├── base.png
├── denses.jpg
├── denses01.png
├── denses02.jpg
├── denses02.png
├── seq2seq.png
└── textcnn.png
/README.md:
--------------------------------------------------------------------------------
1 | # Text Classification Multi-Label: 多标签文本分类
2 | [](https://www.python.org/downloads/release/python-376/)
3 | [](https://www.tensorflow.org/versions/)
4 |
5 |
6 | ## 一、简介
7 | ### 1. 多元分类
8 | 多分类任务中一条数据只有一个标签,但这个标签可能有多种类别。比如判定某个人的性别,只能归类为"男性"、"女性"其中一个。再比如判断一个文本的情感只能归类为"正面"、"中面"或者"负面"其中一个。
9 | ### 2. 多标签分类
10 | 多标签分类任务中一条数据可能有多个标签,每个标签可能有两个或者多个类别。例如,一篇新闻可能同时归类为"娱乐"和"运动",也可能只属于"娱乐"或者其它类别。
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | ## 二、算法
20 |
21 | **4种实现方法**
22 | ```
23 | ├── classifier_multi_label
24 | └── classifier_multi_label
25 | └── classifier_multi_label_textcnn
26 | └── classifier_multi_label_denses
27 | └── classifier_multi_label_seq2seq
28 | ```
29 |
30 | ### 1. classifier_multi_label
31 |
32 |
33 | - 使用BERT第一个token[CLS]的向量,维度为(batch_size,hidden_size)。
34 | - 使用了tf.nn.sigmoid_cross_entropy_with_logits的损失函数。
35 | - 使用了tf.where函数来选择概率小于0.5的对应id。
36 |
37 | ### 2. classifier_multi_label_textcnn
38 |
39 |
40 | - 使用BERT输出的三维向量,维度为(batch_size,sequence_length,hidden_size),然后做为输入进入TextCNN层。
41 | - 使用了tf.nn.sigmoid_cross_entropy_with_logits的损失函数。
42 | - 使用了tf.where函数来选择概率小于0.5的对应id。
43 |
44 | ### 3. classifier_multi_label_denses
45 |
46 |
47 |
48 | - 使用BERT第一个token[CLS]的向量,维度为(batch_size,hidden_size),然后通过多个二分类(全连接层)来解决多标签分类问题。
49 | - 使用了tf.nn.softmax_cross_entropy_with_logits的损失函数。
50 | - 使用了tf.argmax函数来选择输出最高概率。
51 |
52 | ### 4. classifier_multi_label_seq2seq
53 |
54 |
55 | - 使用BERT输出的三维向量,维度为(batch_size,sequence_length,hidden_size),然后做为输入进入seq2seq+attention层。
56 | - 使用了tf.nn.softmax_cross_entropy_with_logits的损失函数。
57 | - 使用了beam search 来解码输出概率。
58 |
59 |
60 |
61 | ## 三、实验
62 | ### 1. 训练过程
63 |
64 |
65 | ### 2. 实验结果
66 |
67 |
68 |
69 | ### 3. 实验结论
70 | - 如果对推理速度的要求不是非常高,基于ALBERT+Seq2Seq_Attention框架的多标签文本分类效果最好。
71 | - 如果对推理速度和模型效果要求都非常高,基于ALBERT+TextCNN会是一个不错的选择。
72 |
73 |
74 |
75 | ## 参考
76 | [多标签文本分类介绍,以及对比训练](https://zhuanlan.zhihu.com/p/152140983)
77 | [多标签文本分类 [ALBERT]](https://zhuanlan.zhihu.com/p/164873441)
78 | [多标签文本分类 [ALBERT+TextCNN]](https://zhuanlan.zhihu.com/p/158622992)
79 | [多标签文本分类 [ALBERT+Multi_Denses]](https://zhuanlan.zhihu.com/p/263573628)
80 | [多标签文本分类 [ALBERT+Seq2Seq+Attention]](https://zhuanlan.zhihu.com/p/260743336)
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/classifier_multi_label/README.md:
--------------------------------------------------------------------------------
1 | # 简介
2 | 1、本项目是在tensorflow版本1.15.0的基础上做的训练和测试。
3 | 2、本项目为中文的多标签文本分类。
4 | 3、欢迎大家联系我 www.hellonlp.com
5 | 4、albert_small_zh_google对应的百度云下载地址:
6 | 链接:https://pan.baidu.com/s/1RKzGJTazlZ7y12YRbAWvyA
7 | 提取码:wuxw
8 |
9 |
10 | # 使用方法
11 | 1、准备数据
12 | 数据格式为:classifier_multi_label/data/test_onehot.csv
13 | 2、参数设置
14 | 参考脚本 hyperparameters.py,直接修改里面的数值即可。
15 | 3、训练
16 | ```
17 | python train.py
18 | ```
19 | 4、预测
20 | ```
21 | python predict.py
22 | ```
23 | 注意:预测时需要把model/save中的模型复制到model/load中,并修改model/load中的checkpoint文件的内容为当前模型名称,例如:model_checkpoint_path: "model_xx_xx.ckpt"。
24 | 。
25 |
26 |
27 |
28 | # 知乎解读
29 | https://zhuanlan.zhihu.com/p/164873441
30 |
31 |
--------------------------------------------------------------------------------
/classifier_multi_label/__init__.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # Copyright 2018 The Google AI Team Authors.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
--------------------------------------------------------------------------------
/classifier_multi_label/albert_small_zh_google/albert_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "attention_probs_dropout_prob": 0.0,
3 | "hidden_act": "gelu",
4 | "hidden_dropout_prob": 0.0,
5 | "embedding_size": 128,
6 | "hidden_size": 384,
7 | "initializer_range": 0.02,
8 | "intermediate_size": 1536,
9 | "max_position_embeddings": 512,
10 | "num_attention_heads": 12,
11 | "num_hidden_layers": 6,
12 | "num_hidden_groups": 1,
13 | "net_structure_type": 0,
14 | "gap_size": 0,
15 | "num_memory_blocks": 0,
16 | "inner_group_num": 1,
17 | "down_scale_factor": 1,
18 | "type_vocab_size": 2,
19 | "vocab_size": 21128
20 | }
--------------------------------------------------------------------------------
/classifier_multi_label/data/test.csv:
--------------------------------------------------------------------------------
1 | content,label
2 | 也没有烧伤,|
3 | 主要是之前在北方呆过几几年,|
4 | 温暖过冬,|
5 | 开箱试了一下看着很不错,|
6 | 还可以给宝宝凉衣服,|
7 | 快递师傅送货迅速,|
8 | 快递员差评,|
9 | 沒有什么噪音,|
10 | 就感觉有一部分是强行捏到一起去的,|
11 | 今天刚到货还没有使用,|
12 | 后面还有个设计,|
13 | 非常好用的宝贝,|
14 | 这个开关我是一直没搞懂,|
15 | 该有的功能都有了,|
16 | 我只说一次,|
17 | 收到l等了15天,|
18 | 所以系统默认好评,|
19 | 但是卖家很快就补发了,|
20 | 现在过了二十天了也没退,|
21 | 冬天有她很暖和,制热效果
22 | 收到第一时间体验,|
23 | 刮花了,|
24 | 也不知道耗不好点,|
25 | 表扬一下物流服务,|
26 | 外观设计:不站地方,|
27 | 换了小米,|
28 | 总之我自己用下来,|
29 | 价格实惠量又足,|
30 | 耗电情况:整体来说耗电不是特别严重,产品功耗
31 | 小兔兔外型还不错了,|
32 | 其他特色:利用小爱或者APP可以智能设定,|
33 | 京东的服务超好,|
34 | 这款取暖器已经买两个了,|
35 | 吹到的风才暖,|
36 | 来的及时,|
37 | 全网最便宜,|
38 | 错过了7天试用,|
39 | 连几十块钱的取暖器都不如,|
40 | 家里有小朋友的建议还是使用空调,|
41 | 定时升温控制都很简单,|
42 | 用了近一月,|
43 | 南方湿冷的冬天是很不错的选择,|
44 | 但是我刚开始使用确实有味道,味道
45 | 说是也转到人工客服,|
46 | 只有在旁边能暖一点,|
47 | 2、结构简单,|
48 | 是想象的模样,|
49 | 1档热风和2档热风,|
50 | 除了物流慢一点点,|
51 | 5分钟屋里就暖和了,制热效果
52 | 碰到长期下雨阴雨的天气,|
53 | 颜色比图片还好看,|
54 | 用起来还可以吧,|
55 | 就是感觉大了一点,|
56 | 安全设计:反正加热中我是一点不敢碰的,|
57 | 尤其自营商品,|
58 | 实在是感人,|
59 | 到现在按钮也开不起,|
60 | 开关,|
61 | 放在客房足够用,|
62 | 一会就暖和,制热效果
63 | 回来赶快拆了包装试下效果,|
64 | 性价比还好吧,|
65 | 使用过后一段时间感觉还不错,|
66 | 但是售后服务太差,|
67 | *元多一点,|
68 | 一如既往地又快又好呀,|
69 | 给东北老家买的,|
70 | 再过几分钟就感觉到开始散热了,|
71 | 整个屋子都很暖静音效果:完全没有声音,声音
72 | 比老款要美观,|
73 | 是划线价的一半多一丢丢,|
74 | 这次再买个送婆婆,|
75 | 小太阳完胜,|
76 | 最重要的是,|
77 | 功能强大发热快,制热效果
78 | 塑料味道太大,味道
79 | 格力原装产品,|
80 | 取暖效果:取暖好,制热效果
81 | 是我们买的第二个了,|
82 | 需要个人安装的也不多,|
83 | 没有任何破损的地方,|
84 | 自营售后服务品质有保障,|
85 | 南方冬天暖气是刚需啊,|
86 | 用了两次我和孩子都喉咙发炎了,|
87 | 十几平方的房间开一夜都没觉得有效果,|
88 | 概念&rdquo,|
89 | 外观设计:很不错取暖效果:很好耗电情况:蛮高的静音效果:很不错安全设计:安全其他特色:没有了,产品功耗/制热效果
90 | 我觉得效果比浴霸好,制热效果
91 | 可根据需要选择功率,|
92 | 身边的朋友都有介绍此款,|
93 | 安全设计:设计独特完美,|
94 | 总体来说物美价廉,|
95 | 放书桌旁干活用很合适,|
96 | 如果属于南方的没暖气喜欢的宝宝可以放心购买,|
97 | 还赠送了晾衣架,|
98 | 贴墙放置,|
99 | 非常的美观还耐用,|
100 | 买了你就后悔,|
101 | 说24小时回电话,|
102 | 家里离不开了,|
103 | 估计到冬天没太阳的时候烘衣服方便,|
104 | 十天用了一百六的电费,产品功耗
105 | 有遥控装置,|
106 | 体积虽小,|
107 | 不把手凑上去都感觉不到他的温度,|
108 | 用了两天的,|
109 | 小房间很快就能加热,|
110 | 当天下单第二天就到,|
111 | 加热很快??,|
112 | 静音效果:二档声音不小,|
113 | 也没有长螺丝刀,|
114 | 平均每天就开两小时,|
115 | 但声音还能接受,|
116 | 静音效果:试用了几天,|
117 | 取暖效果:开到二挡很暖和,制热效果
118 | 今年给老人又买一个,|
119 | 看着小,|
120 | 外形取暖效果:超级棒耗电情况:没在意静音效果:安静舒适安全设计:童锁设计省心,产品功耗
121 | 不知道能不能优化,|
122 | 货到手没有一点损坏,|
123 | 今天就到货了,|
124 | 希望要买好好选别家的,|
125 | 利用热空气上升的自然流动,|
126 | 长的差不多也是200左右,|
127 | 现在穿着睡衣,|
128 | 产品的质量不错,|
129 | 希望能耐用,|
130 | 再也不用担心受凉感冒了,|
131 | 一晚耗电30不是梦,产品功耗
132 | 还会有电火花闪现,|
133 | 有一定的防水,|
134 | 双十二特价很便宜,|
135 | 但是一个小时两度电也是真的,|
136 | 在广州用很合适,|
137 | 大人小朋友不容易烫伤,|
138 | 选择小米唯一吸引我的是支持小爱和手机,|
139 | 着实让我觉得有点生气,|
140 | 主要是家里没有安装暖气片,|
141 | 好多人都和我一样的情况,|
142 | 有风扇设计所致,|
143 | 外观设计:很好看取暖效果:取暖很快效果很好,制热效果
144 | 强卖,|
145 | 还危险了,|
146 | 必须要贴着机器才有点温度,|
147 | 耐用也就有安全保障其他特色:冬天衣服难干,|
148 | 时不时的会咔的一声响,|
149 | 就算开低档温度也不错,|
150 | 挺适合南方的冬天,|
151 | 这东西买的真好,|
152 |
--------------------------------------------------------------------------------
/classifier_multi_label/data/test_onehot.csv:
--------------------------------------------------------------------------------
1 | content,|,互联互通,产品功耗,滑轮提手,声音,APP操控性,呼吸灯,外观,底座,制热范围,遥控器电池,味道,制热效果,衣物烘干,体积大小
2 | 也没有烧伤,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
3 | 主要是之前在北方呆过几几年,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
4 | 温暖过冬,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
5 | 开箱试了一下看着很不错,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
6 | 还可以给宝宝凉衣服,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7 | 快递师傅送货迅速,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
8 | 快递员差评,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
9 | 沒有什么噪音,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
10 | 就感觉有一部分是强行捏到一起去的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
11 | 今天刚到货还没有使用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
12 | 后面还有个设计,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
13 | 非常好用的宝贝,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
14 | 这个开关我是一直没搞懂,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
15 | 该有的功能都有了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
16 | 我只说一次,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
17 | 收到l等了15天,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
18 | 所以系统默认好评,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
19 | 但是卖家很快就补发了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
20 | 现在过了二十天了也没退,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
21 | 冬天有她很暖和,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
22 | 收到第一时间体验,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
23 | 刮花了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
24 | 也不知道耗不好点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
25 | 表扬一下物流服务,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
26 | 外观设计:不站地方,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
27 | 换了小米,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
28 | 总之我自己用下来,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
29 | 价格实惠量又足,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
30 | 耗电情况:整体来说耗电不是特别严重,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0
31 | 小兔兔外型还不错了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
32 | 其他特色:利用小爱或者APP可以智能设定,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
33 | 京东的服务超好,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
34 | 这款取暖器已经买两个了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
35 | 吹到的风才暖,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
36 | 来的及时,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
37 | 全网最便宜,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
38 | 错过了7天试用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
39 | 连几十块钱的取暖器都不如,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
40 | 家里有小朋友的建议还是使用空调,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
41 | 定时升温控制都很简单,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
42 | 用了近一月,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
43 | 南方湿冷的冬天是很不错的选择,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
44 | 但是我刚开始使用确实有味道,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0
45 | 说是也转到人工客服,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
46 | 只有在旁边能暖一点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
47 | 2、结构简单,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
48 | 是想象的模样,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
49 | 1档热风和2档热风,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
50 | 除了物流慢一点点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
51 | 5分钟屋里就暖和了,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
52 | 碰到长期下雨阴雨的天气,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
53 | 颜色比图片还好看,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
54 | 用起来还可以吧,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
55 | 就是感觉大了一点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
56 | 安全设计:反正加热中我是一点不敢碰的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
57 | 尤其自营商品,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
58 | 实在是感人,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
59 | 到现在按钮也开不起,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
60 | 开关,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
61 | 放在客房足够用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
62 | 一会就暖和,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
63 | 回来赶快拆了包装试下效果,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
64 | 性价比还好吧,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
65 | 使用过后一段时间感觉还不错,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
66 | 但是售后服务太差,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
67 | *元多一点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
68 | 一如既往地又快又好呀,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
69 | 给东北老家买的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
70 | 再过几分钟就感觉到开始散热了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
71 | 整个屋子都很暖静音效果:完全没有声音,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0
72 | 比老款要美观,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
73 | 是划线价的一半多一丢丢,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
74 | 这次再买个送婆婆,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
75 | 小太阳完胜,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
76 | 最重要的是,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
77 | 功能强大发热快,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
78 | 塑料味道太大,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0
79 | 格力原装产品,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
80 | 取暖效果:取暖好,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
81 | 是我们买的第二个了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
82 | 需要个人安装的也不多,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
83 | 没有任何破损的地方,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
84 | 自营售后服务品质有保障,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
85 | 南方冬天暖气是刚需啊,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
86 | 用了两次我和孩子都喉咙发炎了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
87 | 十几平方的房间开一夜都没觉得有效果,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
88 | 概念&rdquo,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
89 | 外观设计:很不错取暖效果:很好耗电情况:蛮高的静音效果:很不错安全设计:安全其他特色:没有了,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0
90 | 我觉得效果比浴霸好,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
91 | 可根据需要选择功率,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
92 | 身边的朋友都有介绍此款,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
93 | 安全设计:设计独特完美,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
94 | 总体来说物美价廉,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
95 | 放书桌旁干活用很合适,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
96 | 如果属于南方的没暖气喜欢的宝宝可以放心购买,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
97 | 还赠送了晾衣架,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
98 | 贴墙放置,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
99 | 非常的美观还耐用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
100 | 买了你就后悔,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
101 | 说24小时回电话,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
102 | 家里离不开了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
103 | 估计到冬天没太阳的时候烘衣服方便,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
104 | 十天用了一百六的电费,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0
105 | 有遥控装置,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
106 | 体积虽小,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
107 | 不把手凑上去都感觉不到他的温度,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
108 | 用了两天的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
109 | 小房间很快就能加热,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
110 | 当天下单第二天就到,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
111 | 加热很快??,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
112 | 静音效果:二档声音不小,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
113 | 也没有长螺丝刀,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
114 | 平均每天就开两小时,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
115 | 但声音还能接受,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
116 | 静音效果:试用了几天,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
117 | 取暖效果:开到二挡很暖和,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
118 | 今年给老人又买一个,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
119 | 看着小,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
120 | 外形取暖效果:超级棒耗电情况:没在意静音效果:安静舒适安全设计:童锁设计省心,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0
121 | 不知道能不能优化,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
122 | 货到手没有一点损坏,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
123 | 今天就到货了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
124 | 希望要买好好选别家的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
125 | 利用热空气上升的自然流动,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
126 | 长的差不多也是200左右,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
127 | 现在穿着睡衣,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
128 | 产品的质量不错,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
129 | 希望能耐用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
130 | 再也不用担心受凉感冒了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
131 | 一晚耗电30不是梦,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0
132 | 还会有电火花闪现,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
133 | 有一定的防水,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
134 | 双十二特价很便宜,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
135 | 但是一个小时两度电也是真的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
136 | 在广州用很合适,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
137 | 大人小朋友不容易烫伤,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
138 | 选择小米唯一吸引我的是支持小爱和手机,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
139 | 着实让我觉得有点生气,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
140 | 主要是家里没有安装暖气片,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
141 | 好多人都和我一样的情况,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
142 | 有风扇设计所致,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
143 | 外观设计:很好看取暖效果:取暖很快效果很好,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
144 | 强卖,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
145 | 还危险了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
146 | 必须要贴着机器才有点温度,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
147 | 耐用也就有安全保障其他特色:冬天衣服难干,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
148 | 时不时的会咔的一声响,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
149 | 就算开低档温度也不错,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
150 | 挺适合南方的冬天,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
151 | 这东西买的真好,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
152 |
--------------------------------------------------------------------------------
/classifier_multi_label/data/vocabulary_label.txt:
--------------------------------------------------------------------------------
1 | |
2 | 互联互通
3 | 产品功耗
4 | 滑轮提手
5 | 声音
6 | APP操控性
7 | 呼吸灯
8 | 外观
9 | 底座
10 | 制热范围
11 | 遥控器电池
12 | 味道
13 | 制热效果
14 | 衣物烘干
15 | 体积大小
16 |
--------------------------------------------------------------------------------
/classifier_multi_label/eval.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu Apr 9 10:56:43 2021
4 |
5 | @author: cm
6 | """
7 |
8 |
9 |
10 | # 参考: https://github.com/hellonlp/classifier-multi-label/blob/master/evaluation/eval_multi_label.py
11 |
--------------------------------------------------------------------------------
/classifier_multi_label/hyperparameters.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Mon Nov 12 14:23:12 2021
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import os
10 | pwd = os.path.dirname(os.path.abspath(__file__))
11 | from classifier_multi_label.utils import load_vocabulary
12 |
13 |
14 | class Hyperparamters:
15 | # Train parameters
16 | num_train_epochs = 40
17 | print_step = 10
18 | batch_size = 32
19 | summary_step = 10
20 | num_saved_per_epoch = 3
21 | max_to_keep = 100
22 | logdir = 'logdir/model_01'
23 |
24 | # Model file
25 | file_model_load = 'model/model_load'
26 | file_model_save = 'model/model_save'
27 |
28 | # Train/Test data
29 | data_dir = os.path.join(pwd,'data')
30 | train_data = 'train_onehot.csv'
31 | test_data = 'test_onehot.csv'
32 |
33 | # Load vocabulcary dict
34 | dict_id2label,dict_label2id = load_vocabulary(os.path.join(pwd,'data','vocabulary_label.txt'))
35 | label_vocabulary = list(dict_id2label.values())
36 |
37 | # Optimization parameters
38 | warmup_proportion = 0.1
39 | use_tpu = None
40 | do_lower_case = True
41 | learning_rate = 5e-5
42 |
43 | # TextCNN parameters
44 | num_filters = 128
45 | filter_sizes = [2,3,4,5,6,7]
46 | embedding_size = 384
47 | keep_prob = 0.5
48 |
49 | # Sequence and Label
50 | sequence_length = 60
51 | num_labels = len(list(dict_id2label))
52 |
53 | # ALBERT
54 | model = 'albert_small_zh_google'
55 | bert_path = os.path.join(pwd,model)
56 | vocab_file = os.path.join(pwd,model,'vocab_chinese.txt')
57 | init_checkpoint = os.path.join(pwd,model,'albert_model.ckpt')
58 | saved_model_path = os.path.join(pwd,'model')
59 |
60 |
61 |
62 |
63 |
64 |
65 | if __name__ == '__main__':
66 | hp = Hyperparamters()
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/classifier_multi_label/image/graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label/image/graph.png
--------------------------------------------------------------------------------
/classifier_multi_label/lamb_optimizer.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # Copyright 2018 The Google AI Team Authors.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | # Lint as: python2, python3
16 | """Functions and classes related to optimization (weight updates)."""
17 |
18 | from __future__ import absolute_import
19 | from __future__ import division
20 | from __future__ import print_function
21 |
22 | import re
23 | import six
24 | import tensorflow.compat.v1 as tf
25 |
26 | # pylint: disable=g-direct-tensorflow-import
27 | from tensorflow.python.ops import array_ops
28 | from tensorflow.python.ops import linalg_ops
29 | from tensorflow.python.ops import math_ops
30 | # pylint: enable=g-direct-tensorflow-import
31 |
32 |
33 | class LAMBOptimizer(tf.train.Optimizer):
34 | """LAMB (Layer-wise Adaptive Moments optimizer for Batch training)."""
35 | # A new optimizer that includes correct L2 weight decay, adaptive
36 | # element-wise updating, and layer-wise justification. The LAMB optimizer
37 | # was proposed by Yang You, Jing Li, Jonathan Hseu, Xiaodan Song,
38 | # James Demmel, and Cho-Jui Hsieh in a paper titled as Reducing BERT
39 | # Pre-Training Time from 3 Days to 76 Minutes (arxiv.org/abs/1904.00962)
40 |
41 | def __init__(self,
42 | learning_rate,
43 | weight_decay_rate=0.0,
44 | beta_1=0.9,
45 | beta_2=0.999,
46 | epsilon=1e-6,
47 | exclude_from_weight_decay=None,
48 | exclude_from_layer_adaptation=None,
49 | name="LAMBOptimizer"):
50 | """Constructs a LAMBOptimizer."""
51 | super(LAMBOptimizer, self).__init__(False, name)
52 |
53 | self.learning_rate = learning_rate
54 | self.weight_decay_rate = weight_decay_rate
55 | self.beta_1 = beta_1
56 | self.beta_2 = beta_2
57 | self.epsilon = epsilon
58 | self.exclude_from_weight_decay = exclude_from_weight_decay
59 | # exclude_from_layer_adaptation is set to exclude_from_weight_decay if the
60 | # arg is None.
61 | # TODO(jingli): validate if exclude_from_layer_adaptation is necessary.
62 | if exclude_from_layer_adaptation:
63 | self.exclude_from_layer_adaptation = exclude_from_layer_adaptation
64 | else:
65 | self.exclude_from_layer_adaptation = exclude_from_weight_decay
66 |
67 | def apply_gradients(self, grads_and_vars, global_step=None, name=None):
68 | """See base class."""
69 | assignments = []
70 | for (grad, param) in grads_and_vars:
71 | if grad is None or param is None:
72 | continue
73 |
74 | param_name = self._get_variable_name(param.name)
75 |
76 | m = tf.get_variable(
77 | name=six.ensure_str(param_name) + "/adam_m",
78 | shape=param.shape.as_list(),
79 | dtype=tf.float32,
80 | trainable=False,
81 | initializer=tf.zeros_initializer())
82 | v = tf.get_variable(
83 | name=six.ensure_str(param_name) + "/adam_v",
84 | shape=param.shape.as_list(),
85 | dtype=tf.float32,
86 | trainable=False,
87 | initializer=tf.zeros_initializer())
88 |
89 | # Standard Adam update.
90 | next_m = (
91 | tf.multiply(self.beta_1, m) + tf.multiply(1.0 - self.beta_1, grad))
92 | next_v = (
93 | tf.multiply(self.beta_2, v) + tf.multiply(1.0 - self.beta_2,
94 | tf.square(grad)))
95 |
96 | update = next_m / (tf.sqrt(next_v) + self.epsilon)
97 |
98 | # Just adding the square of the weights to the loss function is *not*
99 | # the correct way of using L2 regularization/weight decay with Adam,
100 | # since that will interact with the m and v parameters in strange ways.
101 | #
102 | # Instead we want ot decay the weights in a manner that doesn't interact
103 | # with the m/v parameters. This is equivalent to adding the square
104 | # of the weights to the loss with plain (non-momentum) SGD.
105 | if self._do_use_weight_decay(param_name):
106 | update += self.weight_decay_rate * param
107 |
108 | ratio = 1.0
109 | if self._do_layer_adaptation(param_name):
110 | w_norm = linalg_ops.norm(param, ord=2)
111 | g_norm = linalg_ops.norm(update, ord=2)
112 | ratio = array_ops.where(math_ops.greater(w_norm, 0), array_ops.where(
113 | math_ops.greater(g_norm, 0), (w_norm / g_norm), 1.0), 1.0)
114 |
115 | update_with_lr = ratio * self.learning_rate * update
116 |
117 | next_param = param - update_with_lr
118 |
119 | assignments.extend(
120 | [param.assign(next_param),
121 | m.assign(next_m),
122 | v.assign(next_v)])
123 | return tf.group(*assignments, name=name)
124 |
125 | def _do_use_weight_decay(self, param_name):
126 | """Whether to use L2 weight decay for `param_name`."""
127 | if not self.weight_decay_rate:
128 | return False
129 | if self.exclude_from_weight_decay:
130 | for r in self.exclude_from_weight_decay:
131 | if re.search(r, param_name) is not None:
132 | return False
133 | return True
134 |
135 | def _do_layer_adaptation(self, param_name):
136 | """Whether to do layer-wise learning rate adaptation for `param_name`."""
137 | if self.exclude_from_layer_adaptation:
138 | for r in self.exclude_from_layer_adaptation:
139 | if re.search(r, param_name) is not None:
140 | return False
141 | return True
142 |
143 | def _get_variable_name(self, param_name):
144 | """Get the variable name from the tensor name."""
145 | m = re.match("^(.*):\\d+$", six.ensure_str(param_name))
146 | if m is not None:
147 | param_name = m.group(1)
148 | return param_name
149 |
--------------------------------------------------------------------------------
/classifier_multi_label/logdir/model_01/events.out.tfevents.1595940723.DESKTOP-QC1A83I:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label/logdir/model_01/events.out.tfevents.1595940723.DESKTOP-QC1A83I
--------------------------------------------------------------------------------
/classifier_multi_label/model/model_load/README.md:
--------------------------------------------------------------------------------
1 | # 训练过程保存的模型
2 |
3 | ## 模型路径
4 | - model_save/checkpoit
5 | - model_save/model_xx_0.ckpt.data-00000-of-00001
6 | - model_save/model_xx_0.ckpt.index
7 | - model_save/model_xx_0.ckpt.meta
8 |
9 |
10 | ## checkpoint内容
11 | ```
12 | model_checkpoint_path: "model_xx_0.ckpt"
13 | ```
14 |
--------------------------------------------------------------------------------
/classifier_multi_label/model/model_save/READEME.md:
--------------------------------------------------------------------------------
1 | # 推理所用模型
2 |
--------------------------------------------------------------------------------
/classifier_multi_label/modules.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu May 30 21:01:45 2021
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import tensorflow as tf
10 |
11 | from classifier_multi_label.hyperparameters import Hyperparamters as hp
12 |
13 |
14 | def cell_textcnn(inputs,is_training):
15 | # Add a dimension in final shape
16 | inputs_expand = tf.expand_dims(inputs, -1)
17 | # Create a convolution + maxpool layer for each filter size
18 | pooled_outputs = []
19 | with tf.name_scope("TextCNN"):
20 | for i, filter_size in enumerate(hp.filter_sizes):
21 | with tf.name_scope("conv-maxpool-%s" % filter_size):
22 | # Convolution Layer
23 | filter_shape = [filter_size, hp.embedding_size, 1, hp.num_filters]
24 | W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1),dtype=tf.float32, name="W")
25 | b = tf.Variable(tf.constant(0.1, shape=[hp.num_filters]),dtype=tf.float32, name="b")
26 | conv = tf.nn.conv2d(
27 | inputs_expand,
28 | W,
29 | strides=[1, 1, 1, 1],
30 | padding="VALID",
31 | name="conv")
32 | # Apply nonlinearity
33 | h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
34 | # Maxpooling over the outputs
35 | pooled = tf.nn.max_pool(
36 | h,
37 | ksize=[1, hp.sequence_length - filter_size + 1, 1, 1],
38 | strides=[1, 1, 1, 1],
39 | padding='VALID',
40 | name="pool")
41 | pooled_outputs.append(pooled)
42 | # Combine all the pooled features
43 | num_filters_total = hp.num_filters * len(hp.filter_sizes)
44 | h_pool = tf.concat(pooled_outputs, 3)
45 | h_pool_flat = tf.reshape(h_pool, [-1, num_filters_total])
46 | # Dropout
47 | h_pool_flat_dropout = tf.nn.dropout(h_pool_flat, keep_prob=hp.keep_prob if is_training else 1)
48 | return h_pool_flat_dropout
49 |
50 |
51 |
--------------------------------------------------------------------------------
/classifier_multi_label/networks.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu May 30 20:44:42 2019
4 |
5 | @author: cm
6 | """
7 |
8 | import os
9 | import tensorflow as tf
10 | from classifier_multi_label import modeling
11 | from classifier_multi_label import optimization
12 | from classifier_multi_label.utils import time_now_string
13 | from classifier_multi_label.hyperparameters import Hyperparamters as hp
14 | from classifier_multi_label.classifier_utils import ClassifyProcessor
15 |
16 |
17 | num_labels = hp.num_labels
18 | processor = ClassifyProcessor()
19 | bert_config_file = os.path.join(hp.bert_path,'albert_config.json')
20 | bert_config = modeling.AlbertConfig.from_json_file(bert_config_file)
21 |
22 |
23 |
24 | class NetworkAlbert(object):
25 | def __init__(self,is_training):
26 | # Training or not
27 | self.is_training = is_training
28 |
29 | # Placeholder
30 | self.input_ids = tf.placeholder(tf.int32, shape=[None, hp.sequence_length], name='input_ids')
31 | self.input_masks = tf.placeholder(tf.int32, shape=[None, hp.sequence_length], name='input_masks')
32 | self.segment_ids = tf.placeholder(tf.int32, shape=[None, hp.sequence_length], name='segment_ids')
33 | self.label_ids = tf.placeholder(tf.float32, shape=[None,hp.num_labels], name='label_ids')
34 |
35 | # Load BERT model
36 | self.model = modeling.AlbertModel(
37 | config=bert_config,
38 | is_training=self.is_training,
39 | input_ids=self.input_ids,
40 | input_mask=self.input_masks,
41 | token_type_ids=self.segment_ids,
42 | use_one_hot_embeddings=False)
43 |
44 | # Get the feature vector by BERT
45 | output_layer = self.model.get_pooled_output()
46 |
47 | # Hidden size
48 | hidden_size = output_layer.shape[-1].value
49 |
50 | with tf.name_scope("Full-connection"):
51 | output_weights = tf.get_variable(
52 | "output_weights", [num_labels, hidden_size],
53 | initializer=tf.truncated_normal_initializer(stddev=0.02))
54 | output_bias = tf.get_variable(
55 | "output_bias", [num_labels], initializer=tf.zeros_initializer())
56 | logits = tf.nn.bias_add(tf.matmul(output_layer, output_weights, transpose_b=True), output_bias)
57 | # Prediction sigmoid(Multi-label)
58 | self.probabilities = tf.nn.sigmoid(logits)
59 |
60 |
61 | with tf.variable_scope("Prediction"):
62 | # Prediction
63 | zero = tf.zeros_like(self.probabilities)
64 | one = tf.ones_like(self.probabilities)
65 | self.predictions = tf.where(self.probabilities < 0.5, x=zero, y=one)
66 |
67 | with tf.variable_scope("loss"):
68 | # Summary for tensorboard
69 | if self.is_training:
70 | self.accuracy = tf.reduce_mean(tf.to_float(tf.equal(self.predictions, self.label_ids)))
71 | tf.summary.scalar('accuracy', self.accuracy)
72 |
73 | # Initial embedding by BERT
74 | ckpt = tf.train.get_checkpoint_state(hp.saved_model_path)
75 | checkpoint_suffix = ".index"
76 | if ckpt and tf.gfile.Exists(ckpt.model_checkpoint_path + checkpoint_suffix):
77 | print('='*10,'Restoring model from checkpoint!','='*10)
78 | print("%s - Restoring model from checkpoint ~%s" % (time_now_string(),
79 | ckpt.model_checkpoint_path))
80 | else:
81 | print('='*10,'First time load BERT model!','='*10)
82 | tvars = tf.trainable_variables()
83 | if hp.init_checkpoint:
84 | (assignment_map, initialized_variable_names) = \
85 | modeling.get_assignment_map_from_checkpoint(tvars,
86 | hp.init_checkpoint)
87 | tf.train.init_from_checkpoint(hp.init_checkpoint, assignment_map)
88 |
89 | # Loss and Optimizer
90 | if self.is_training:
91 | # Global_step
92 | self.global_step = tf.Variable(0, name='global_step', trainable=False)
93 | per_example_loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=self.label_ids,logits=logits)
94 | self.loss = tf.reduce_mean(per_example_loss)
95 |
96 | # Optimizer BERT
97 | train_examples = processor.get_train_examples(hp.data_dir)
98 | num_train_steps = int(
99 | len(train_examples) / hp.batch_size * hp.num_train_epochs)
100 | #num_train_steps = 10000
101 | num_warmup_steps = int(num_train_steps * hp.warmup_proportion)
102 | print('num_train_steps',num_train_steps)
103 | self.optimizer = optimization.create_optimizer(self.loss,
104 | hp.learning_rate,
105 | num_train_steps,
106 | num_warmup_steps,
107 | hp.use_tpu,
108 | Global_step=self.global_step)
109 |
110 | # Summary for tensorboard
111 | tf.summary.scalar('loss', self.loss)
112 | self.merged = tf.summary.merge_all()
113 |
114 |
115 |
116 | if __name__ == '__main__':
117 | # Load model
118 | albert = NetworkAlbert(is_training=True)
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/classifier_multi_label/optimization.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """Functions and classes related to optimization (weight updates)."""
3 |
4 | from __future__ import absolute_import
5 | from __future__ import division
6 | from __future__ import print_function
7 | import re
8 | import six
9 | from six.moves import zip
10 | import tensorflow.compat.v1 as tf
11 | from tensorflow.contrib import tpu as contrib_tpu
12 | from classifier_multi_label import lamb_optimizer
13 |
14 |
15 | def create_optimizer(loss, init_lr, num_train_steps, num_warmup_steps, use_tpu,Global_step,
16 | optimizer="adamw", poly_power=1.0, start_warmup_step=0):
17 | """Creates an optimizer training op."""
18 | #global_step = tf.train.get_or_create_global_step()
19 |
20 | # by chenming
21 | if Global_step:
22 | global_step = Global_step
23 | else:
24 | global_step = tf.train.get_or_create_global_step()
25 |
26 | learning_rate = tf.constant(value=init_lr, shape=[], dtype=tf.float32)
27 |
28 | # Implements linear decay of the learning rate.
29 | learning_rate = tf.train.polynomial_decay(
30 | learning_rate,
31 | global_step,
32 | num_train_steps,
33 | end_learning_rate=0.0,
34 | power=poly_power,
35 | cycle=False)
36 |
37 | # Implements linear warmup. I.e., if global_step - start_warmup_step <
38 | # num_warmup_steps, the learning rate will be
39 | # `(global_step - start_warmup_step)/num_warmup_steps * init_lr`.
40 | if num_warmup_steps:
41 | tf.logging.info("++++++ warmup starts at step " + str(start_warmup_step)
42 | + ", for " + str(num_warmup_steps) + " steps ++++++")
43 | global_steps_int = tf.cast(global_step, tf.int32)
44 | start_warm_int = tf.constant(start_warmup_step, dtype=tf.int32)
45 | global_steps_int = global_steps_int - start_warm_int
46 | warmup_steps_int = tf.constant(num_warmup_steps, dtype=tf.int32)
47 |
48 | global_steps_float = tf.cast(global_steps_int, tf.float32)
49 | warmup_steps_float = tf.cast(warmup_steps_int, tf.float32)
50 |
51 | warmup_percent_done = global_steps_float / warmup_steps_float
52 | warmup_learning_rate = init_lr * warmup_percent_done
53 |
54 | is_warmup = tf.cast(global_steps_int < warmup_steps_int, tf.float32)
55 | learning_rate = (
56 | (1.0 - is_warmup) * learning_rate + is_warmup * warmup_learning_rate)
57 |
58 | # It is OK that you use this optimizer for finetuning, since this
59 | # is how the model was trained (note that the Adam m/v variables are NOT
60 | # loaded from init_checkpoint.)
61 | # It is OK to use AdamW in the finetuning even the model is trained by LAMB.
62 | # As report in the Bert pulic github, the learning rate for SQuAD 1.1 finetune
63 | # is 3e-5, 4e-5 or 5e-5. For LAMB, the users can use 3e-4, 4e-4,or 5e-4 for a
64 | # batch size of 64 in the finetune.
65 | if optimizer == "adamw":
66 | tf.logging.info("using adamw")
67 | optimizer = AdamWeightDecayOptimizer(
68 | learning_rate=learning_rate,
69 | weight_decay_rate=0.01,
70 | beta_1=0.9,
71 | beta_2=0.999,
72 | epsilon=1e-6,
73 | exclude_from_weight_decay=["LayerNorm", "layer_norm", "bias"])
74 | elif optimizer == "lamb":
75 | tf.logging.info("using lamb")
76 | optimizer = lamb_optimizer.LAMBOptimizer(
77 | learning_rate=learning_rate,
78 | weight_decay_rate=0.01,
79 | beta_1=0.9,
80 | beta_2=0.999,
81 | epsilon=1e-6,
82 | exclude_from_weight_decay=["LayerNorm", "layer_norm", "bias"])
83 | else:
84 | raise ValueError("Not supported optimizer: ", optimizer)
85 |
86 | if use_tpu:
87 | optimizer = contrib_tpu.CrossShardOptimizer(optimizer)
88 |
89 | tvars = tf.trainable_variables()
90 | grads = tf.gradients(loss, tvars)
91 |
92 | # This is how the model was pre-trained.
93 | (grads, _) = tf.clip_by_global_norm(grads, clip_norm=1.0)
94 |
95 | train_op = optimizer.apply_gradients(
96 | list(zip(grads, tvars)), global_step=global_step)
97 |
98 | # Normally the global step update is done inside of `apply_gradients`.
99 | # However, neither `AdamWeightDecayOptimizer` nor `LAMBOptimizer` do this.
100 | # But if you use a different optimizer, you should probably take this line
101 | # out.
102 | new_global_step = global_step + 1
103 | train_op = tf.group(train_op, [global_step.assign(new_global_step)])
104 | return train_op
105 |
106 |
107 | class AdamWeightDecayOptimizer(tf.train.Optimizer):
108 | """A basic Adam optimizer that includes "correct" L2 weight decay."""
109 |
110 | def __init__(self,
111 | learning_rate,
112 | weight_decay_rate=0.0,
113 | beta_1=0.9,
114 | beta_2=0.999,
115 | epsilon=1e-6,
116 | exclude_from_weight_decay=None,
117 | name="AdamWeightDecayOptimizer"):
118 | """Constructs a AdamWeightDecayOptimizer."""
119 | super(AdamWeightDecayOptimizer, self).__init__(False, name)
120 |
121 | self.learning_rate = learning_rate
122 | self.weight_decay_rate = weight_decay_rate
123 | self.beta_1 = beta_1
124 | self.beta_2 = beta_2
125 | self.epsilon = epsilon
126 | self.exclude_from_weight_decay = exclude_from_weight_decay
127 |
128 | def apply_gradients(self, grads_and_vars, global_step=None, name=None):
129 | """See base class."""
130 | assignments = []
131 | for (grad, param) in grads_and_vars:
132 | if grad is None or param is None:
133 | continue
134 |
135 | param_name = self._get_variable_name(param.name)
136 |
137 | m = tf.get_variable(
138 | name=six.ensure_str(param_name) + "/adam_m",
139 | shape=param.shape.as_list(),
140 | dtype=tf.float32,
141 | trainable=False,
142 | initializer=tf.zeros_initializer())
143 | v = tf.get_variable(
144 | name=six.ensure_str(param_name) + "/adam_v",
145 | shape=param.shape.as_list(),
146 | dtype=tf.float32,
147 | trainable=False,
148 | initializer=tf.zeros_initializer())
149 |
150 | # Standard Adam update.
151 | next_m = (
152 | tf.multiply(self.beta_1, m) + tf.multiply(1.0 - self.beta_1, grad))
153 | next_v = (
154 | tf.multiply(self.beta_2, v) + tf.multiply(1.0 - self.beta_2,
155 | tf.square(grad)))
156 |
157 | update = next_m / (tf.sqrt(next_v) + self.epsilon)
158 |
159 | # Just adding the square of the weights to the loss function is *not*
160 | # the correct way of using L2 regularization/weight decay with Adam,
161 | # since that will interact with the m and v parameters in strange ways.
162 | #
163 | # Instead we want ot decay the weights in a manner that doesn't interact
164 | # with the m/v parameters. This is equivalent to adding the square
165 | # of the weights to the loss with plain (non-momentum) SGD.
166 | if self._do_use_weight_decay(param_name):
167 | update += self.weight_decay_rate * param
168 |
169 | update_with_lr = self.learning_rate * update
170 |
171 | next_param = param - update_with_lr
172 |
173 | assignments.extend(
174 | [param.assign(next_param),
175 | m.assign(next_m),
176 | v.assign(next_v)])
177 | return tf.group(*assignments, name=name)
178 |
179 | def _do_use_weight_decay(self, param_name):
180 | """Whether to use L2 weight decay for `param_name`."""
181 | if not self.weight_decay_rate:
182 | return False
183 | if self.exclude_from_weight_decay:
184 | for r in self.exclude_from_weight_decay:
185 | if re.search(r, param_name) is not None:
186 | return False
187 | return True
188 |
189 | def _get_variable_name(self, param_name):
190 | """Get the variable name from the tensor name."""
191 | m = re.match("^(.*):\\d+$", six.ensure_str(param_name))
192 | if m is not None:
193 | param_name = m.group(1)
194 | return param_name
195 |
--------------------------------------------------------------------------------
/classifier_multi_label/predict.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu May 30 17:12:37 2021
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import os
10 | #os.environ["CUDA_VISIBLE_DEVICES"] = '-1'
11 | pwd = os.path.dirname(os.path.abspath(__file__))
12 | import sys
13 | sys.path.append(os.path.dirname(os.path.dirname(__file__)))
14 | import numpy as np
15 | import tensorflow as tf
16 | from classifier_multi_label.networks import NetworkAlbert
17 | from classifier_multi_label.classifier_utils import get_feature_test,id2label
18 | from classifier_multi_label.hyperparameters import Hyperparamters as hp
19 |
20 |
21 | class Model(object,):
22 | """
23 | Load NetworkAlbert model
24 | """
25 | def __init__(self):
26 | self.albert, self.sess = self.load_model()
27 | @staticmethod
28 | def load_model():
29 | with tf.Graph().as_default():
30 | sess = tf.Session()
31 | with sess.as_default():
32 | albert = NetworkAlbert(is_training=False)
33 | saver = tf.train.Saver()
34 | sess.run(tf.global_variables_initializer())
35 | checkpoint_dir = os.path.abspath(os.path.join(pwd,hp.file_model_load))
36 | print (checkpoint_dir)
37 | ckpt = tf.train.get_checkpoint_state(checkpoint_dir)
38 | saver.restore(sess, ckpt.model_checkpoint_path)
39 | return albert,sess
40 |
41 | MODEL = Model()
42 | print('Load model finished!')
43 |
44 |
45 | def get_label(sentence):
46 | """
47 | Prediction of the sentence's label.
48 | """
49 | feature = get_feature_test(sentence)
50 | fd = {MODEL.albert.input_ids: [feature[0]],
51 | MODEL.albert.input_masks: [feature[1]],
52 | MODEL.albert.segment_ids:[feature[2]],
53 | }
54 | prediction = MODEL.sess.run(MODEL.albert.predictions, feed_dict=fd)[0]
55 | return [id2label(l) for l in np.where(prediction==1)[0] if l!=0]
56 |
57 |
58 | if __name__ == '__main__':
59 | # Test
60 | sentences = ['耗电情况:整体来说耗电不是特别严重',
61 | '取暖效果:取暖效果好',
62 | '取暖效果:开到二挡很暖和',
63 | '一个小时房间仍然没暖和',
64 | '开着坐旁边才能暖和']
65 | for sentence in sentences:
66 | print(sentence,get_label(sentence))
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/classifier_multi_label/train.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu May 30 21:42:07 2021
4 |
5 | @author: cm
6 | """
7 |
8 | import os
9 | #os.environ["CUDA_VISIBLE_DEVICES"] = '-1'
10 | import numpy as np
11 | import tensorflow as tf
12 | from classifier_multi_label.networks import NetworkAlbert
13 | from classifier_multi_label.classifier_utils import get_features
14 | from classifier_multi_label.hyperparameters import Hyperparamters as hp
15 | from classifier_multi_label.utils import select,shuffle_one,time_now_string
16 |
17 |
18 | pwd = os.path.dirname(os.path.abspath(__file__))
19 | MODEL = NetworkAlbert(is_training=True)
20 |
21 |
22 | # Get data features
23 | input_ids,input_masks,segment_ids,label_ids = get_features()
24 | num_train_samples = len(input_ids)
25 | indexs = np.arange(num_train_samples)
26 | num_batchs = int((num_train_samples - 1) /hp.batch_size) + 1
27 | print('Number of batch:',num_batchs)
28 |
29 | # Set up the graph
30 | saver = tf.train.Saver(max_to_keep=hp.max_to_keep)
31 | sess = tf.Session()
32 | sess.run(tf.global_variables_initializer())
33 |
34 | # Load model saved before
35 | MODEL_SAVE_PATH = os.path.join(pwd, hp.file_model_save)
36 | ckpt = tf.train.get_checkpoint_state(MODEL_SAVE_PATH)
37 | if ckpt and ckpt.model_checkpoint_path:
38 | saver.restore(sess, ckpt.model_checkpoint_path)
39 | print('Restored model!')
40 |
41 |
42 | with sess.as_default():
43 | # Tensorboard writer
44 | writer = tf.summary.FileWriter(hp.logdir, sess.graph)
45 | for i in range(hp.num_train_epochs):
46 | np.random.shuffle(indexs)
47 | for j in range(num_batchs-1):
48 | # Get ids selected
49 | i1 = indexs[j * hp.batch_size:min((j + 1) * hp.batch_size, num_train_samples)]
50 |
51 | # Get features
52 | input_id_ = select(input_ids,i1)
53 | input_mask_ = select(input_masks,i1)
54 | segment_id_ = select(segment_ids,i1)
55 | label_id_ = select(label_ids,i1)
56 |
57 | # Feed dict
58 | fd = {MODEL.input_ids: input_id_,
59 | MODEL.input_masks: input_mask_,
60 | MODEL.segment_ids:segment_id_,
61 | MODEL.label_ids:label_id_}
62 |
63 | # Optimizer
64 | sess.run(MODEL.optimizer, feed_dict = fd)
65 |
66 | # Tensorboard
67 | if j%hp.summary_step==0:
68 | summary,glolal_step = sess.run([MODEL.merged,MODEL.global_step], feed_dict = fd)
69 | writer.add_summary(summary, glolal_step)
70 |
71 | # Save Model
72 | if j%(num_batchs//hp.num_saved_per_epoch)==0:
73 | if not os.path.exists(os.path.join(pwd, hp.file_model_save)):
74 | os.makedirs(os.path.join(pwd, hp.file_model_save))
75 | saver.save(sess, os.path.join(pwd, hp.file_model_save, 'model'+'_%s_%s.ckpt'%(str(i),str(j))))
76 |
77 | # Log
78 | if j % hp.print_step == 0:
79 | fd = {MODEL.input_ids: input_id_,
80 | MODEL.input_masks: input_mask_,
81 | MODEL.segment_ids:segment_id_,
82 | MODEL.label_ids:label_id_}
83 | loss = sess.run(MODEL.loss, feed_dict = fd)
84 | print('Time:%s, Epoch:%s, Batch number:%s/%s, Loss:%s'%(time_now_string(),str(i),str(j),str(num_batchs),str(loss)))
85 | print('Train finished')
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/classifier_multi_label/utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Fri May 25 23:43:39 2021
4 | @author: cm
5 | """
6 |
7 |
8 | import time
9 | import numpy as np
10 | import pandas as pd
11 |
12 |
13 |
14 | def time_now_string():
15 | return time.strftime("%Y-%m-%d %H:%M:%S",time.localtime( time.time() ))
16 |
17 |
18 | def select(data,ids):
19 | return [data[i] for i in ids]
20 |
21 |
22 | def shuffle_one(a1):
23 | ran = np.arange(len(a1))
24 | np.random.shuffle(ran)
25 | return np.array(a1)[ran].tolist()
26 |
27 |
28 | def shuffle_two(a1,a2):
29 | ran = np.arange(len(a1))
30 | np.random.shuffle(ran)
31 | return [a1[l] for l in ran], [a2[l] for l in ran]
32 |
33 |
34 | def load_txt(file):
35 | with open(file,encoding='utf-8',errors='ignore') as fp:
36 | lines = fp.readlines()
37 | lines = [l.strip() for l in lines]
38 | print("Load data from file (%s) finished !"%file)
39 | return lines
40 |
41 |
42 | def save_txt(file,lines):
43 | lines = [l+'\n' for l in lines]
44 | with open(file,'w+',encoding='utf-8') as fp:#a+添加
45 | fp.writelines(lines)
46 | return "Write data to txt finished !"
47 |
48 |
49 | def load_csv(file,encoding='utf-8',header=0):
50 | return pd.read_csv(file,encoding=encoding,header=header,error_bad_lines=False)#,encoding='gbk'
51 |
52 |
53 | def save_csv(dataframe,file,header=True,index=None,encoding="utf-8-sig"):
54 | return dataframe.to_csv(file,
55 | mode='w+',
56 | header=header,
57 | index=index,
58 | encoding=encoding)
59 |
60 |
61 | def save_excel(dataframe,file,header=True,sheetname='Sheet1'):
62 | return dataframe.to_excel(file,
63 | header=header,
64 | sheet_name=sheetname)
65 |
66 |
67 | def load_excel(file,header=0):
68 | return pd.read_excel(file,
69 | header=header,
70 | )
71 |
72 |
73 | def cut_list(data,size):
74 | """
75 | data: a list
76 | size: the size of cut
77 | """
78 | return [data[i * size:min((i + 1) * size, len(data))] for i in range(int(len(data)-1)//size + 1)]
79 |
80 |
81 |
82 | def load_vocabulary(file_vocabulary_label):
83 | """
84 | Load vocabulary to dict
85 | """
86 | vocabulary = load_txt(file_vocabulary_label)
87 | dict_id2label,dict_label2id = {},{}
88 | for i,l in enumerate(vocabulary):
89 | dict_id2label[str(i)] = str(l)
90 | dict_label2id[str(l)] = str(i)
91 | return dict_id2label,dict_label2id
92 |
93 |
94 |
95 | if __name__ == '__main__':
96 | print('')
97 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/README.md:
--------------------------------------------------------------------------------
1 | # 简介
2 | 1、本项目是在tensorflow版本1.15.0的基础上做的训练和测试。
3 | 2、本项目为中文的多标签文本分类。
4 | 3、欢迎大家联系我 www.hellonlp.com
5 | 4、albert_small_zh_google对应的百度云下载地址:
6 | 链接:https://pan.baidu.com/s/1RKzGJTazlZ7y12YRbAWvyA
7 | 提取码:wuxw
8 |
9 | # 使用方法
10 | 1、准备数据
11 | 数据格式为:classifier_multi_label_denses/data/test_onehot.csv
12 | 2、参数设置
13 | 参考脚本 hyperparameters.py,直接修改里面的数值即可。
14 | 3、训练
15 | ```
16 | python train.py
17 | ```
18 | 4、预测
19 | ```
20 | python predict.py
21 | ```
22 | 注意:推理时需要把model/save中的模型复制到model/load中,并修改model/load中的checkpoint文件的内容为当前模型名称,例如:model_checkpoint_path: "model_xx_xx.ckpt"。
23 | 。
24 |
25 |
26 | # 知乎解读
27 | https://zhuanlan.zhihu.com/p/263573628
28 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/__init__.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # Copyright 2018 The Google AI Team Authors.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/albert_small_zh_google/albert_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "attention_probs_dropout_prob": 0.0,
3 | "hidden_act": "gelu",
4 | "hidden_dropout_prob": 0.0,
5 | "embedding_size": 128,
6 | "hidden_size": 384,
7 | "initializer_range": 0.02,
8 | "intermediate_size": 1536,
9 | "max_position_embeddings": 512,
10 | "num_attention_heads": 12,
11 | "num_hidden_layers": 6,
12 | "num_hidden_groups": 1,
13 | "net_structure_type": 0,
14 | "gap_size": 0,
15 | "num_memory_blocks": 0,
16 | "inner_group_num": 1,
17 | "down_scale_factor": 1,
18 | "type_vocab_size": 2,
19 | "vocab_size": 21128
20 | }
--------------------------------------------------------------------------------
/classifier_multi_label_denses/data/test.csv:
--------------------------------------------------------------------------------
1 | content,label
2 | 也没有烧伤,|
3 | 主要是之前在北方呆过几几年,|
4 | 温暖过冬,|
5 | 开箱试了一下看着很不错,|
6 | 还可以给宝宝凉衣服,|
7 | 快递师傅送货迅速,|
8 | 快递员差评,|
9 | 沒有什么噪音,|
10 | 就感觉有一部分是强行捏到一起去的,|
11 | 今天刚到货还没有使用,|
12 | 后面还有个设计,|
13 | 非常好用的宝贝,|
14 | 这个开关我是一直没搞懂,|
15 | 该有的功能都有了,|
16 | 我只说一次,|
17 | 收到l等了15天,|
18 | 所以系统默认好评,|
19 | 但是卖家很快就补发了,|
20 | 现在过了二十天了也没退,|
21 | 冬天有她很暖和,制热效果
22 | 收到第一时间体验,|
23 | 刮花了,|
24 | 也不知道耗不好点,|
25 | 表扬一下物流服务,|
26 | 外观设计:不站地方,|
27 | 换了小米,|
28 | 总之我自己用下来,|
29 | 价格实惠量又足,|
30 | 耗电情况:整体来说耗电不是特别严重,产品功耗
31 | 小兔兔外型还不错了,|
32 | 其他特色:利用小爱或者APP可以智能设定,|
33 | 京东的服务超好,|
34 | 这款取暖器已经买两个了,|
35 | 吹到的风才暖,|
36 | 来的及时,|
37 | 全网最便宜,|
38 | 错过了7天试用,|
39 | 连几十块钱的取暖器都不如,|
40 | 家里有小朋友的建议还是使用空调,|
41 | 定时升温控制都很简单,|
42 | 用了近一月,|
43 | 南方湿冷的冬天是很不错的选择,|
44 | 但是我刚开始使用确实有味道,味道
45 | 说是也转到人工客服,|
46 | 只有在旁边能暖一点,|
47 | 2、结构简单,|
48 | 是想象的模样,|
49 | 1档热风和2档热风,|
50 | 除了物流慢一点点,|
51 | 5分钟屋里就暖和了,制热效果
52 | 碰到长期下雨阴雨的天气,|
53 | 颜色比图片还好看,|
54 | 用起来还可以吧,|
55 | 就是感觉大了一点,|
56 | 安全设计:反正加热中我是一点不敢碰的,|
57 | 尤其自营商品,|
58 | 实在是感人,|
59 | 到现在按钮也开不起,|
60 | 开关,|
61 | 放在客房足够用,|
62 | 一会就暖和,制热效果
63 | 回来赶快拆了包装试下效果,|
64 | 性价比还好吧,|
65 | 使用过后一段时间感觉还不错,|
66 | 但是售后服务太差,|
67 | *元多一点,|
68 | 一如既往地又快又好呀,|
69 | 给东北老家买的,|
70 | 再过几分钟就感觉到开始散热了,|
71 | 整个屋子都很暖静音效果:完全没有声音,声音
72 | 比老款要美观,|
73 | 是划线价的一半多一丢丢,|
74 | 这次再买个送婆婆,|
75 | 小太阳完胜,|
76 | 最重要的是,|
77 | 功能强大发热快,制热效果
78 | 塑料味道太大,味道
79 | 格力原装产品,|
80 | 取暖效果:取暖好,制热效果
81 | 是我们买的第二个了,|
82 | 需要个人安装的也不多,|
83 | 没有任何破损的地方,|
84 | 自营售后服务品质有保障,|
85 | 南方冬天暖气是刚需啊,|
86 | 用了两次我和孩子都喉咙发炎了,|
87 | 十几平方的房间开一夜都没觉得有效果,|
88 | 概念&rdquo,|
89 | 外观设计:很不错取暖效果:很好耗电情况:蛮高的静音效果:很不错安全设计:安全其他特色:没有了,产品功耗/制热效果
90 | 我觉得效果比浴霸好,制热效果
91 | 可根据需要选择功率,|
92 | 身边的朋友都有介绍此款,|
93 | 安全设计:设计独特完美,|
94 | 总体来说物美价廉,|
95 | 放书桌旁干活用很合适,|
96 | 如果属于南方的没暖气喜欢的宝宝可以放心购买,|
97 | 还赠送了晾衣架,|
98 | 贴墙放置,|
99 | 非常的美观还耐用,|
100 | 买了你就后悔,|
101 | 说24小时回电话,|
102 | 家里离不开了,|
103 | 估计到冬天没太阳的时候烘衣服方便,|
104 | 十天用了一百六的电费,产品功耗
105 | 有遥控装置,|
106 | 体积虽小,|
107 | 不把手凑上去都感觉不到他的温度,|
108 | 用了两天的,|
109 | 小房间很快就能加热,|
110 | 当天下单第二天就到,|
111 | 加热很快??,|
112 | 静音效果:二档声音不小,|
113 | 也没有长螺丝刀,|
114 | 平均每天就开两小时,|
115 | 但声音还能接受,|
116 | 静音效果:试用了几天,|
117 | 取暖效果:开到二挡很暖和,制热效果
118 | 今年给老人又买一个,|
119 | 看着小,|
120 | 外形取暖效果:超级棒耗电情况:没在意静音效果:安静舒适安全设计:童锁设计省心,产品功耗
121 | 不知道能不能优化,|
122 | 货到手没有一点损坏,|
123 | 今天就到货了,|
124 | 希望要买好好选别家的,|
125 | 利用热空气上升的自然流动,|
126 | 长的差不多也是200左右,|
127 | 现在穿着睡衣,|
128 | 产品的质量不错,|
129 | 希望能耐用,|
130 | 再也不用担心受凉感冒了,|
131 | 一晚耗电30不是梦,产品功耗
132 | 还会有电火花闪现,|
133 | 有一定的防水,|
134 | 双十二特价很便宜,|
135 | 但是一个小时两度电也是真的,|
136 | 在广州用很合适,|
137 | 大人小朋友不容易烫伤,|
138 | 选择小米唯一吸引我的是支持小爱和手机,|
139 | 着实让我觉得有点生气,|
140 | 主要是家里没有安装暖气片,|
141 | 好多人都和我一样的情况,|
142 | 有风扇设计所致,|
143 | 外观设计:很好看取暖效果:取暖很快效果很好,制热效果
144 | 强卖,|
145 | 还危险了,|
146 | 必须要贴着机器才有点温度,|
147 | 耐用也就有安全保障其他特色:冬天衣服难干,|
148 | 时不时的会咔的一声响,|
149 | 就算开低档温度也不错,|
150 | 挺适合南方的冬天,|
151 | 这东西买的真好,|
152 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/data/test_onehot.csv:
--------------------------------------------------------------------------------
1 | content,|,互联互通,产品功耗,滑轮提手,声音,APP操控性,呼吸灯,外观,底座,制热范围,遥控器电池,味道,制热效果,衣物烘干,体积大小
2 | 也没有烧伤,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
3 | 主要是之前在北方呆过几几年,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
4 | 温暖过冬,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
5 | 开箱试了一下看着很不错,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
6 | 还可以给宝宝凉衣服,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7 | 快递师傅送货迅速,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
8 | 快递员差评,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
9 | 沒有什么噪音,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
10 | 就感觉有一部分是强行捏到一起去的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
11 | 今天刚到货还没有使用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
12 | 后面还有个设计,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
13 | 非常好用的宝贝,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
14 | 这个开关我是一直没搞懂,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
15 | 该有的功能都有了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
16 | 我只说一次,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
17 | 收到l等了15天,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
18 | 所以系统默认好评,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
19 | 但是卖家很快就补发了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
20 | 现在过了二十天了也没退,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
21 | 冬天有她很暖和,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
22 | 收到第一时间体验,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
23 | 刮花了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
24 | 也不知道耗不好点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
25 | 表扬一下物流服务,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
26 | 外观设计:不站地方,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
27 | 换了小米,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
28 | 总之我自己用下来,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
29 | 价格实惠量又足,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
30 | 耗电情况:整体来说耗电不是特别严重,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0
31 | 小兔兔外型还不错了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
32 | 其他特色:利用小爱或者APP可以智能设定,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
33 | 京东的服务超好,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
34 | 这款取暖器已经买两个了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
35 | 吹到的风才暖,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
36 | 来的及时,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
37 | 全网最便宜,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
38 | 错过了7天试用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
39 | 连几十块钱的取暖器都不如,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
40 | 家里有小朋友的建议还是使用空调,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
41 | 定时升温控制都很简单,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
42 | 用了近一月,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
43 | 南方湿冷的冬天是很不错的选择,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
44 | 但是我刚开始使用确实有味道,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0
45 | 说是也转到人工客服,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
46 | 只有在旁边能暖一点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
47 | 2、结构简单,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
48 | 是想象的模样,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
49 | 1档热风和2档热风,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
50 | 除了物流慢一点点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
51 | 5分钟屋里就暖和了,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
52 | 碰到长期下雨阴雨的天气,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
53 | 颜色比图片还好看,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
54 | 用起来还可以吧,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
55 | 就是感觉大了一点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
56 | 安全设计:反正加热中我是一点不敢碰的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
57 | 尤其自营商品,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
58 | 实在是感人,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
59 | 到现在按钮也开不起,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
60 | 开关,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
61 | 放在客房足够用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
62 | 一会就暖和,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
63 | 回来赶快拆了包装试下效果,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
64 | 性价比还好吧,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
65 | 使用过后一段时间感觉还不错,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
66 | 但是售后服务太差,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
67 | *元多一点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
68 | 一如既往地又快又好呀,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
69 | 给东北老家买的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
70 | 再过几分钟就感觉到开始散热了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
71 | 整个屋子都很暖静音效果:完全没有声音,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0
72 | 比老款要美观,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
73 | 是划线价的一半多一丢丢,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
74 | 这次再买个送婆婆,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
75 | 小太阳完胜,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
76 | 最重要的是,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
77 | 功能强大发热快,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
78 | 塑料味道太大,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0
79 | 格力原装产品,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
80 | 取暖效果:取暖好,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
81 | 是我们买的第二个了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
82 | 需要个人安装的也不多,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
83 | 没有任何破损的地方,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
84 | 自营售后服务品质有保障,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
85 | 南方冬天暖气是刚需啊,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
86 | 用了两次我和孩子都喉咙发炎了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
87 | 十几平方的房间开一夜都没觉得有效果,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
88 | 概念&rdquo,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
89 | 外观设计:很不错取暖效果:很好耗电情况:蛮高的静音效果:很不错安全设计:安全其他特色:没有了,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0
90 | 我觉得效果比浴霸好,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
91 | 可根据需要选择功率,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
92 | 身边的朋友都有介绍此款,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
93 | 安全设计:设计独特完美,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
94 | 总体来说物美价廉,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
95 | 放书桌旁干活用很合适,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
96 | 如果属于南方的没暖气喜欢的宝宝可以放心购买,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
97 | 还赠送了晾衣架,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
98 | 贴墙放置,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
99 | 非常的美观还耐用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
100 | 买了你就后悔,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
101 | 说24小时回电话,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
102 | 家里离不开了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
103 | 估计到冬天没太阳的时候烘衣服方便,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
104 | 十天用了一百六的电费,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0
105 | 有遥控装置,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
106 | 体积虽小,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
107 | 不把手凑上去都感觉不到他的温度,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
108 | 用了两天的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
109 | 小房间很快就能加热,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
110 | 当天下单第二天就到,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
111 | 加热很快??,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
112 | 静音效果:二档声音不小,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
113 | 也没有长螺丝刀,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
114 | 平均每天就开两小时,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
115 | 但声音还能接受,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
116 | 静音效果:试用了几天,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
117 | 取暖效果:开到二挡很暖和,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
118 | 今年给老人又买一个,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
119 | 看着小,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
120 | 外形取暖效果:超级棒耗电情况:没在意静音效果:安静舒适安全设计:童锁设计省心,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0
121 | 不知道能不能优化,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
122 | 货到手没有一点损坏,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
123 | 今天就到货了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
124 | 希望要买好好选别家的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
125 | 利用热空气上升的自然流动,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
126 | 长的差不多也是200左右,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
127 | 现在穿着睡衣,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
128 | 产品的质量不错,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
129 | 希望能耐用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
130 | 再也不用担心受凉感冒了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
131 | 一晚耗电30不是梦,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0
132 | 还会有电火花闪现,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
133 | 有一定的防水,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
134 | 双十二特价很便宜,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
135 | 但是一个小时两度电也是真的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
136 | 在广州用很合适,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
137 | 大人小朋友不容易烫伤,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
138 | 选择小米唯一吸引我的是支持小爱和手机,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
139 | 着实让我觉得有点生气,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
140 | 主要是家里没有安装暖气片,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
141 | 好多人都和我一样的情况,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
142 | 有风扇设计所致,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
143 | 外观设计:很好看取暖效果:取暖很快效果很好,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
144 | 强卖,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
145 | 还危险了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
146 | 必须要贴着机器才有点温度,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
147 | 耐用也就有安全保障其他特色:冬天衣服难干,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
148 | 时不时的会咔的一声响,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
149 | 就算开低档温度也不错,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
150 | 挺适合南方的冬天,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
151 | 这东西买的真好,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
152 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/data/vocabulary_label.txt:
--------------------------------------------------------------------------------
1 | |
2 | 互联互通
3 | 产品功耗
4 | 滑轮提手
5 | 声音
6 | APP操控性
7 | 呼吸灯
8 | 外观
9 | 底座
10 | 制热范围
11 | 遥控器电池
12 | 味道
13 | 制热效果
14 | 衣物烘干
15 | 体积大小
16 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/eval.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu Apr 9 10:56:43 2021
4 |
5 | @author: cm
6 | """
7 |
8 |
9 |
10 | # 参考: https://github.com/hellonlp/classifier-multi-label/blob/master/evaluation/eval_multi_label.py
11 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/hyperparameters.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Mon Nov 12 14:23:12 2020
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import os
10 | import sys
11 | pwd = os.path.dirname(os.path.abspath(__file__))
12 | sys.path.append(pwd)
13 | from classifier_multi_label_denses.utils import load_vocabulary
14 |
15 |
16 | class Hyperparamters:
17 | # Train parameters
18 | num_train_epochs = 5
19 | print_step = 100
20 | batch_size = 8#64
21 | summary_step = 10
22 | num_saved_per_epoch = 3
23 | max_to_keep = 100
24 | logdir = 'logdir/CML_Denses'
25 |
26 | # Model paths
27 | file_model_save = 'model/model_save'
28 | file_model_load = 'model/model_load'
29 |
30 | # Train/Test data
31 | data_dir = os.path.join(pwd,'data')
32 | train_data = 'train_onehot.csv'
33 | test_data = 'test_onehot.csv'
34 |
35 | # Load vocabulcary dict
36 | dict_id2label,dict_label2id = load_vocabulary(os.path.join(pwd,'data','vocabulary_label.txt') )
37 | label_vocabulary = list(dict_id2label.values())
38 |
39 | # Optimization parameters
40 | warmup_proportion = 0.1
41 | use_tpu = None
42 | do_lower_case = True
43 | learning_rate = 5e-5
44 |
45 |
46 | # Sequence and Label
47 | sequence_length = 60
48 | num_labels = len(list(dict_id2label))
49 |
50 | # ALBERT
51 | model = 'albert_small_zh_google'
52 | bert_path = os.path.join(pwd,model)
53 | vocab_file = os.path.join(pwd,model,'vocab_chinese.txt')
54 | init_checkpoint = os.path.join(pwd,model,'albert_model.ckpt')
55 | saved_model_path = os.path.join(pwd,'model')
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/lamb_optimizer.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 |
3 | """Functions and classes related to optimization (weight updates)."""
4 |
5 | from __future__ import absolute_import
6 | from __future__ import division
7 | from __future__ import print_function
8 |
9 | import re
10 | import six
11 | import tensorflow.compat.v1 as tf
12 |
13 | # pylint: disable=g-direct-tensorflow-import
14 | from tensorflow.python.ops import array_ops
15 | from tensorflow.python.ops import linalg_ops
16 | from tensorflow.python.ops import math_ops
17 | # pylint: enable=g-direct-tensorflow-import
18 |
19 |
20 | class LAMBOptimizer(tf.train.Optimizer):
21 | """LAMB (Layer-wise Adaptive Moments optimizer for Batch training)."""
22 | # A new optimizer that includes correct L2 weight decay, adaptive
23 | # element-wise updating, and layer-wise justification. The LAMB optimizer
24 | # was proposed by Yang You, Jing Li, Jonathan Hseu, Xiaodan Song,
25 | # James Demmel, and Cho-Jui Hsieh in a paper titled as Reducing BERT
26 | # Pre-Training Time from 3 Days to 76 Minutes (arxiv.org/abs/1904.00962)
27 |
28 | def __init__(self,
29 | learning_rate,
30 | weight_decay_rate=0.0,
31 | beta_1=0.9,
32 | beta_2=0.999,
33 | epsilon=1e-6,
34 | exclude_from_weight_decay=None,
35 | exclude_from_layer_adaptation=None,
36 | name="LAMBOptimizer"):
37 | """Constructs a LAMBOptimizer."""
38 | super(LAMBOptimizer, self).__init__(False, name)
39 |
40 | self.learning_rate = learning_rate
41 | self.weight_decay_rate = weight_decay_rate
42 | self.beta_1 = beta_1
43 | self.beta_2 = beta_2
44 | self.epsilon = epsilon
45 | self.exclude_from_weight_decay = exclude_from_weight_decay
46 | # exclude_from_layer_adaptation is set to exclude_from_weight_decay if the
47 | # arg is None.
48 | # TODO(jingli): validate if exclude_from_layer_adaptation is necessary.
49 | if exclude_from_layer_adaptation:
50 | self.exclude_from_layer_adaptation = exclude_from_layer_adaptation
51 | else:
52 | self.exclude_from_layer_adaptation = exclude_from_weight_decay
53 |
54 | def apply_gradients(self, grads_and_vars, global_step=None, name=None):
55 | """See base class."""
56 | assignments = []
57 | for (grad, param) in grads_and_vars:
58 | if grad is None or param is None:
59 | continue
60 |
61 | param_name = self._get_variable_name(param.name)
62 |
63 | m = tf.get_variable(
64 | name=six.ensure_str(param_name) + "/adam_m",
65 | shape=param.shape.as_list(),
66 | dtype=tf.float32,
67 | trainable=False,
68 | initializer=tf.zeros_initializer())
69 | v = tf.get_variable(
70 | name=six.ensure_str(param_name) + "/adam_v",
71 | shape=param.shape.as_list(),
72 | dtype=tf.float32,
73 | trainable=False,
74 | initializer=tf.zeros_initializer())
75 |
76 | # Standard Adam update.
77 | next_m = (
78 | tf.multiply(self.beta_1, m) + tf.multiply(1.0 - self.beta_1, grad))
79 | next_v = (
80 | tf.multiply(self.beta_2, v) + tf.multiply(1.0 - self.beta_2,
81 | tf.square(grad)))
82 |
83 | update = next_m / (tf.sqrt(next_v) + self.epsilon)
84 |
85 | # Just adding the square of the weights to the loss function is *not*
86 | # the correct way of using L2 regularization/weight decay with Adam,
87 | # since that will interact with the m and v parameters in strange ways.
88 | #
89 | # Instead we want ot decay the weights in a manner that doesn't interact
90 | # with the m/v parameters. This is equivalent to adding the square
91 | # of the weights to the loss with plain (non-momentum) SGD.
92 | if self._do_use_weight_decay(param_name):
93 | update += self.weight_decay_rate * param
94 |
95 | ratio = 1.0
96 | if self._do_layer_adaptation(param_name):
97 | w_norm = linalg_ops.norm(param, ord=2)
98 | g_norm = linalg_ops.norm(update, ord=2)
99 | ratio = array_ops.where(math_ops.greater(w_norm, 0), array_ops.where(
100 | math_ops.greater(g_norm, 0), (w_norm / g_norm), 1.0), 1.0)
101 |
102 | update_with_lr = ratio * self.learning_rate * update
103 |
104 | next_param = param - update_with_lr
105 |
106 | assignments.extend(
107 | [param.assign(next_param),
108 | m.assign(next_m),
109 | v.assign(next_v)])
110 | return tf.group(*assignments, name=name)
111 |
112 | def _do_use_weight_decay(self, param_name):
113 | """Whether to use L2 weight decay for `param_name`."""
114 | if not self.weight_decay_rate:
115 | return False
116 | if self.exclude_from_weight_decay:
117 | for r in self.exclude_from_weight_decay:
118 | if re.search(r, param_name) is not None:
119 | return False
120 | return True
121 |
122 | def _do_layer_adaptation(self, param_name):
123 | """Whether to do layer-wise learning rate adaptation for `param_name`."""
124 | if self.exclude_from_layer_adaptation:
125 | for r in self.exclude_from_layer_adaptation:
126 | if re.search(r, param_name) is not None:
127 | return False
128 | return True
129 |
130 | def _get_variable_name(self, param_name):
131 | """Get the variable name from the tensor name."""
132 | m = re.match("^(.*):\\d+$", six.ensure_str(param_name))
133 | if m is not None:
134 | param_name = m.group(1)
135 | return param_name
136 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/logdir/CML_Denses/events.out.tfevents.1602241580.DESKTOP-QC1A83I:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_denses/logdir/CML_Denses/events.out.tfevents.1602241580.DESKTOP-QC1A83I
--------------------------------------------------------------------------------
/classifier_multi_label_denses/model/model_load/README.md:
--------------------------------------------------------------------------------
1 | # 推理所用的模型
2 |
3 | ## 模型路径
4 | - model_load/checkpoit
5 | - model_load/model_xx_0.ckpt.data-00000-of-00001
6 | - model_load/model_xx_0.ckpt.index
7 | - model_load/model_xx_0.ckpt.meta
8 |
9 | ## checkpoint内容
10 | ```
11 | model_checkpoint_path: "model_xx_0.ckpt"
12 | ```
13 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/model/model_save/README.md:
--------------------------------------------------------------------------------
1 | # 训练过程所得模型
2 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/networks.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu May 30 20:44:42 2021
4 |
5 | @author: cm
6 | """
7 |
8 | import os
9 | import tensorflow as tf
10 |
11 | from classifier_multi_label_denses import modeling
12 | from classifier_multi_label_denses import optimization
13 | from classifier_multi_label_denses.utils import time_now_string
14 | from classifier_multi_label_denses.hyperparameters import Hyperparamters as hp
15 | from classifier_multi_label_denses.classifier_utils import ClassifyProcessor
16 |
17 |
18 | num_labels = hp.num_labels
19 | processor = ClassifyProcessor()
20 | bert_config_file = os.path.join(hp.bert_path,'albert_config.json')
21 | bert_config = modeling.AlbertConfig.from_json_file(bert_config_file)
22 |
23 |
24 | class NetworkAlbert(object):
25 | def __init__(self,is_training):
26 | # Training or not
27 | self.is_training = is_training
28 |
29 | # Placeholder
30 | self.input_ids = tf.placeholder(tf.int32, shape=[None, hp.sequence_length], name='input_ids')
31 | self.input_masks = tf.placeholder(tf.int32, shape=[None, hp.sequence_length], name='input_masks')
32 | self.segment_ids = tf.placeholder(tf.int32, shape=[None, hp.sequence_length], name='segment_ids')
33 | self.label_ids = tf.placeholder(tf.int32, shape=[None,hp.num_labels], name='label_ids')
34 |
35 | # Load BERT model
36 | self.model = modeling.AlbertModel(
37 | config=bert_config,
38 | is_training=self.is_training,
39 | input_ids=self.input_ids,
40 | input_mask=self.input_masks,
41 | token_type_ids=self.segment_ids,
42 | use_one_hot_embeddings=False)
43 |
44 |
45 | # Get the feature vector by BERT
46 | output_layer = self.model.get_pooled_output()
47 | print('output_layer',output_layer)#(?, 384)
48 |
49 | # Hidden size
50 | hidden_size = output_layer.shape[-1].value
51 |
52 | with tf.name_scope("Full-connection"):
53 | loss_num_label = []
54 | logits_num_label = []
55 | for i in range(hp.num_labels):
56 | output_weights = tf.get_variable(
57 | "output_weights%s"%str(i), [2, hidden_size],
58 | initializer=tf.truncated_normal_initializer(stddev=0.02))
59 | output_bias = tf.get_variable(
60 | "output_bias%s"%str(i), [2], initializer=tf.zeros_initializer())
61 | logits = tf.matmul(output_layer, output_weights, transpose_b=True)
62 | logits = tf.nn.bias_add(logits, output_bias)
63 | logits_num_label.append(logits)
64 | one_hot_labels = tf.one_hot(self.label_ids[:,i], depth=2, dtype=tf.int32)
65 | per_example_loss = tf.nn.softmax_cross_entropy_with_logits(labels=one_hot_labels,logits=logits)
66 | loss_num_label.append(tf.reduce_mean(per_example_loss))
67 | self.logits_num_label = tf.transpose(tf.stack(logits_num_label, 0),[1,0,2])
68 | self.loss_num_label = tf.stack(loss_num_label, 0)
69 | self.probabilities = tf.nn.sigmoid(self.logits_num_label)
70 |
71 | with tf.variable_scope("Prediction"):
72 | # Prediction
73 | self.predictions = tf.to_int32(tf.argmax(self.probabilities,2))
74 |
75 | with tf.variable_scope("loss"):
76 | # Summary for tensorboard
77 | if self.is_training:
78 | self.accuracy = tf.reduce_mean(tf.to_float(tf.equal(self.predictions, self.label_ids)))
79 | tf.summary.scalar('accuracy', self.accuracy)
80 |
81 | # Initial embedding by BERT
82 | ckpt = tf.train.get_checkpoint_state(hp.saved_model_path)
83 | checkpoint_suffix = ".index"
84 | if ckpt and tf.gfile.Exists(ckpt.model_checkpoint_path + checkpoint_suffix):
85 | print('='*10,'Restoring model from checkpoint!','='*10)
86 | print("%s - Restoring model from checkpoint ~%s" % (time_now_string(),
87 | ckpt.model_checkpoint_path))
88 | else:
89 | print('='*10,'First time load BERT model!','='*10)
90 | tvars = tf.trainable_variables()
91 | if hp.init_checkpoint:
92 | (assignment_map, initialized_variable_names) = \
93 | modeling.get_assignment_map_from_checkpoint(tvars,
94 | hp.init_checkpoint)
95 | tf.train.init_from_checkpoint(hp.init_checkpoint, assignment_map)
96 |
97 | # Loss and Optimizer
98 | if self.is_training:
99 | # Global_step
100 | self.global_step = tf.Variable(0, name='global_step', trainable=False)
101 | self.loss = tf.reduce_mean(self.loss_num_label)
102 |
103 | # Optimizer BERT
104 | train_examples = processor.get_train_examples(hp.data_dir)
105 | num_train_steps = int(
106 | len(train_examples) / hp.batch_size * hp.num_train_epochs)
107 | num_warmup_steps = int(num_train_steps * hp.warmup_proportion)
108 | print('num_train_steps',num_train_steps)
109 | self.optimizer = optimization.create_optimizer(self.loss,
110 | hp.learning_rate,
111 | num_train_steps,
112 | num_warmup_steps,
113 | hp.use_tpu,
114 | Global_step=self.global_step)
115 |
116 | # Summary for tensorboard
117 | tf.summary.scalar('loss', self.loss)
118 | self.merged = tf.summary.merge_all()
119 |
120 |
121 | if __name__ == '__main__':
122 | # Load model
123 | albert = NetworkAlbert(is_training=True)
124 |
125 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/optimization.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 |
3 | """Functions and classes related to optimization (weight updates)."""
4 |
5 | from __future__ import absolute_import
6 | from __future__ import division
7 | from __future__ import print_function
8 | import re
9 | import six
10 | from six.moves import zip
11 | import tensorflow.compat.v1 as tf
12 | from tensorflow.contrib import tpu as contrib_tpu
13 | from classifier_multi_label_denses import lamb_optimizer
14 |
15 |
16 |
17 | def create_optimizer(loss, init_lr, num_train_steps, num_warmup_steps, use_tpu,Global_step,
18 | optimizer="adamw", poly_power=1.0, start_warmup_step=0):
19 | """Creates an optimizer training op."""
20 |
21 | if Global_step:
22 | global_step = Global_step
23 | else:
24 | global_step = tf.train.get_or_create_global_step()
25 | #global_step = tf.train.get_or_create_global_step()
26 |
27 | learning_rate = tf.constant(value=init_lr, shape=[], dtype=tf.float32)
28 |
29 | # Implements linear decay of the learning rate.
30 | learning_rate = tf.train.polynomial_decay(
31 | learning_rate,
32 | global_step,
33 | num_train_steps,
34 | end_learning_rate=0.0,
35 | power=poly_power,
36 | cycle=False)
37 |
38 | # Implements linear warmup. I.e., if global_step - start_warmup_step <
39 | # num_warmup_steps, the learning rate will be
40 | # `(global_step - start_warmup_step)/num_warmup_steps * init_lr`.
41 | if num_warmup_steps:
42 | tf.logging.info("++++++ warmup starts at step " + str(start_warmup_step)
43 | + ", for " + str(num_warmup_steps) + " steps ++++++")
44 | global_steps_int = tf.cast(global_step, tf.int32)
45 | start_warm_int = tf.constant(start_warmup_step, dtype=tf.int32)
46 | global_steps_int = global_steps_int - start_warm_int
47 | warmup_steps_int = tf.constant(num_warmup_steps, dtype=tf.int32)
48 |
49 | global_steps_float = tf.cast(global_steps_int, tf.float32)
50 | warmup_steps_float = tf.cast(warmup_steps_int, tf.float32)
51 |
52 | warmup_percent_done = global_steps_float / warmup_steps_float
53 | warmup_learning_rate = init_lr * warmup_percent_done
54 |
55 | is_warmup = tf.cast(global_steps_int < warmup_steps_int, tf.float32)
56 | learning_rate = (
57 | (1.0 - is_warmup) * learning_rate + is_warmup * warmup_learning_rate)
58 |
59 | # It is OK that you use this optimizer for finetuning, since this
60 | # is how the model was trained (note that the Adam m/v variables are NOT
61 | # loaded from init_checkpoint.)
62 | # It is OK to use AdamW in the finetuning even the model is trained by LAMB.
63 | # As report in the Bert pulic github, the learning rate for SQuAD 1.1 finetune
64 | # is 3e-5, 4e-5 or 5e-5. For LAMB, the users can use 3e-4, 4e-4,or 5e-4 for a
65 | # batch size of 64 in the finetune.
66 | if optimizer == "adamw":
67 | tf.logging.info("using adamw")
68 | optimizer = AdamWeightDecayOptimizer(
69 | learning_rate=learning_rate,
70 | weight_decay_rate=0.01,
71 | beta_1=0.9,
72 | beta_2=0.999,
73 | epsilon=1e-6,
74 | exclude_from_weight_decay=["LayerNorm", "layer_norm", "bias"])
75 | elif optimizer == "lamb":
76 | tf.logging.info("using lamb")
77 | optimizer = lamb_optimizer.LAMBOptimizer(
78 | learning_rate=learning_rate,
79 | weight_decay_rate=0.01,
80 | beta_1=0.9,
81 | beta_2=0.999,
82 | epsilon=1e-6,
83 | exclude_from_weight_decay=["LayerNorm", "layer_norm", "bias"])
84 | else:
85 | raise ValueError("Not supported optimizer: ", optimizer)
86 |
87 | if use_tpu:
88 | optimizer = contrib_tpu.CrossShardOptimizer(optimizer)
89 |
90 | tvars = tf.trainable_variables()
91 | grads = tf.gradients(loss, tvars)
92 |
93 | # This is how the model was pre-trained.
94 | (grads, _) = tf.clip_by_global_norm(grads, clip_norm=1.0)
95 |
96 | train_op = optimizer.apply_gradients(
97 | list(zip(grads, tvars)), global_step=global_step)
98 |
99 | # Normally the global step update is done inside of `apply_gradients`.
100 | # However, neither `AdamWeightDecayOptimizer` nor `LAMBOptimizer` do this.
101 | # But if you use a different optimizer, you should probably take this line
102 | # out.
103 | new_global_step = global_step + 1
104 | train_op = tf.group(train_op, [global_step.assign(new_global_step)])
105 | return train_op
106 |
107 |
108 | class AdamWeightDecayOptimizer(tf.train.Optimizer):
109 | """A basic Adam optimizer that includes "correct" L2 weight decay."""
110 |
111 | def __init__(self,
112 | learning_rate,
113 | weight_decay_rate=0.0,
114 | beta_1=0.9,
115 | beta_2=0.999,
116 | epsilon=1e-6,
117 | exclude_from_weight_decay=None,
118 | name="AdamWeightDecayOptimizer"):
119 | """Constructs a AdamWeightDecayOptimizer."""
120 | super(AdamWeightDecayOptimizer, self).__init__(False, name)
121 |
122 | self.learning_rate = learning_rate
123 | self.weight_decay_rate = weight_decay_rate
124 | self.beta_1 = beta_1
125 | self.beta_2 = beta_2
126 | self.epsilon = epsilon
127 | self.exclude_from_weight_decay = exclude_from_weight_decay
128 |
129 | def apply_gradients(self, grads_and_vars, global_step=None, name=None):
130 | """See base class."""
131 | assignments = []
132 | for (grad, param) in grads_and_vars:
133 | if grad is None or param is None:
134 | continue
135 |
136 | param_name = self._get_variable_name(param.name)
137 |
138 | m = tf.get_variable(
139 | name=six.ensure_str(param_name) + "/adam_m",
140 | shape=param.shape.as_list(),
141 | dtype=tf.float32,
142 | trainable=False,
143 | initializer=tf.zeros_initializer())
144 | v = tf.get_variable(
145 | name=six.ensure_str(param_name) + "/adam_v",
146 | shape=param.shape.as_list(),
147 | dtype=tf.float32,
148 | trainable=False,
149 | initializer=tf.zeros_initializer())
150 |
151 | # Standard Adam update.
152 | next_m = (
153 | tf.multiply(self.beta_1, m) + tf.multiply(1.0 - self.beta_1, grad))
154 | next_v = (
155 | tf.multiply(self.beta_2, v) + tf.multiply(1.0 - self.beta_2,
156 | tf.square(grad)))
157 |
158 | update = next_m / (tf.sqrt(next_v) + self.epsilon)
159 |
160 | # Just adding the square of the weights to the loss function is *not*
161 | # the correct way of using L2 regularization/weight decay with Adam,
162 | # since that will interact with the m and v parameters in strange ways.
163 | #
164 | # Instead we want ot decay the weights in a manner that doesn't interact
165 | # with the m/v parameters. This is equivalent to adding the square
166 | # of the weights to the loss with plain (non-momentum) SGD.
167 | if self._do_use_weight_decay(param_name):
168 | update += self.weight_decay_rate * param
169 |
170 | update_with_lr = self.learning_rate * update
171 |
172 | next_param = param - update_with_lr
173 |
174 | assignments.extend(
175 | [param.assign(next_param),
176 | m.assign(next_m),
177 | v.assign(next_v)])
178 | return tf.group(*assignments, name=name)
179 |
180 | def _do_use_weight_decay(self, param_name):
181 | """Whether to use L2 weight decay for `param_name`."""
182 | if not self.weight_decay_rate:
183 | return False
184 | if self.exclude_from_weight_decay:
185 | for r in self.exclude_from_weight_decay:
186 | if re.search(r, param_name) is not None:
187 | return False
188 | return True
189 |
190 | def _get_variable_name(self, param_name):
191 | """Get the variable name from the tensor name."""
192 | m = re.match("^(.*):\\d+$", six.ensure_str(param_name))
193 | if m is not None:
194 | param_name = m.group(1)
195 | return param_name
196 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/predict.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu May 30 17:12:37 2019
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import os
10 | import sys
11 | pwd = os.path.dirname(os.path.abspath(__file__))
12 | sys.path.append(os.path.dirname(os.path.dirname(__file__)))
13 | import numpy as np
14 | import tensorflow as tf
15 | from classifier_multi_label_denses.networks import NetworkAlbert
16 | from classifier_multi_label_denses.classifier_utils import get_feature_test,id2label
17 | from classifier_multi_label_denses.hyperparameters import Hyperparamters as hp
18 |
19 |
20 |
21 | class ModelAlbertDenses(object,):
22 | """
23 | Load NetworkAlbertDenses
24 | """
25 | def __init__(self):
26 | self.albert, self.sess = self.load_model()
27 | @staticmethod
28 | def load_model():
29 | with tf.Graph().as_default():
30 | sess = tf.Session()
31 | out_dir = os.path.join(pwd, "model")
32 | with sess.as_default():
33 | albert = NetworkAlbert(is_training=False)
34 | saver = tf.train.Saver()
35 | sess.run(tf.global_variables_initializer())
36 | checkpoint_dir = os.path.abspath(os.path.join(out_dir,hp.file_model_load))
37 | print (checkpoint_dir)
38 | ckpt = tf.train.get_checkpoint_state(checkpoint_dir)
39 | saver.restore(sess, ckpt.model_checkpoint_path)
40 | return albert,sess
41 |
42 | MODEL = ModelAlbertDenses()
43 | print('Load model finished!')
44 |
45 |
46 |
47 |
48 | def get_label(sentence):
49 | """
50 | Prediction of the sentence's label.
51 | """
52 | feature = get_feature_test(sentence)
53 | fd = {MODEL.albert.input_ids: [feature[0]],
54 | MODEL.albert.input_masks: [feature[1]],
55 | MODEL.albert.segment_ids:[feature[2]],
56 | }
57 | prediction = MODEL.sess.run(MODEL.albert.predictions, feed_dict=fd)[0]
58 | return [id2label(l) for l in np.where(prediction==1)[0] if l!=0]
59 |
60 |
61 |
62 | if __name__ == '__main__':
63 | ##
64 | sentences = ['外形外观:好看',
65 | '有股难闻的怪味',
66 | '制热效果很好',
67 | '开一晚上也感觉不到热']
68 | for sentence in sentences:
69 | print(get_label(sentence))
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/tokenization.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 |
3 | """Tokenization classes."""
4 |
5 | from __future__ import absolute_import
6 | from __future__ import division
7 | from __future__ import print_function
8 |
9 | import collections
10 | import re
11 | import unicodedata
12 | import six
13 | from six.moves import range
14 | import tensorflow.compat.v1 as tf
15 | #import tensorflow_hub as hub
16 | import sentencepiece as spm
17 |
18 | SPIECE_UNDERLINE = u"▁".encode("utf-8")
19 |
20 |
21 | def validate_case_matches_checkpoint(do_lower_case, init_checkpoint):
22 | """Checks whether the casing config is consistent with the checkpoint name."""
23 |
24 | # The casing has to be passed in by the user and there is no explicit check
25 | # as to whether it matches the checkpoint. The casing information probably
26 | # should have been stored in the bert_config.json file, but it's not, so
27 | # we have to heuristically detect it to validate.
28 |
29 | if not init_checkpoint:
30 | return
31 |
32 | m = re.match("^.*?([A-Za-z0-9_-]+)/bert_model.ckpt",
33 | six.ensure_str(init_checkpoint))
34 | if m is None:
35 | return
36 |
37 | model_name = m.group(1)
38 |
39 | lower_models = [
40 | "uncased_L-24_H-1024_A-16", "uncased_L-12_H-768_A-12",
41 | "multilingual_L-12_H-768_A-12", "chinese_L-12_H-768_A-12"
42 | ]
43 |
44 | cased_models = [
45 | "cased_L-12_H-768_A-12", "cased_L-24_H-1024_A-16",
46 | "multi_cased_L-12_H-768_A-12"
47 | ]
48 |
49 | is_bad_config = False
50 | if model_name in lower_models and not do_lower_case:
51 | is_bad_config = True
52 | actual_flag = "False"
53 | case_name = "lowercased"
54 | opposite_flag = "True"
55 |
56 | if model_name in cased_models and do_lower_case:
57 | is_bad_config = True
58 | actual_flag = "True"
59 | case_name = "cased"
60 | opposite_flag = "False"
61 |
62 | if is_bad_config:
63 | raise ValueError(
64 | "You passed in `--do_lower_case=%s` with `--init_checkpoint=%s`. "
65 | "However, `%s` seems to be a %s model, so you "
66 | "should pass in `--do_lower_case=%s` so that the fine-tuning matches "
67 | "how the model was pre-training. If this error is wrong, please "
68 | "just comment out this check." % (actual_flag, init_checkpoint,
69 | model_name, case_name, opposite_flag))
70 |
71 |
72 | def preprocess_text(inputs, remove_space=True, lower=False):
73 | """preprocess data by removing extra space and normalize data."""
74 | outputs = inputs
75 | if remove_space:
76 | outputs = " ".join(inputs.strip().split())
77 |
78 | if six.PY2 and isinstance(outputs, str):
79 | try:
80 | outputs = six.ensure_text(outputs, "utf-8")
81 | except UnicodeDecodeError:
82 | outputs = six.ensure_text(outputs, "latin-1")
83 |
84 | outputs = unicodedata.normalize("NFKD", outputs)
85 | outputs = "".join([c for c in outputs if not unicodedata.combining(c)])
86 | if lower:
87 | outputs = outputs.lower()
88 |
89 | return outputs
90 |
91 |
92 | def encode_pieces(sp_model, text, return_unicode=True, sample=False):
93 | """turn sentences into word pieces."""
94 |
95 | if six.PY2 and isinstance(text, six.text_type):
96 | text = six.ensure_binary(text, "utf-8")
97 |
98 | if not sample:
99 | pieces = sp_model.EncodeAsPieces(text)
100 | else:
101 | pieces = sp_model.SampleEncodeAsPieces(text, 64, 0.1)
102 | new_pieces = []
103 | for piece in pieces:
104 | piece = printable_text(piece)
105 | if len(piece) > 1 and piece[-1] == "," and piece[-2].isdigit():
106 | cur_pieces = sp_model.EncodeAsPieces(
107 | six.ensure_binary(piece[:-1]).replace(SPIECE_UNDERLINE, b""))
108 | if piece[0] != SPIECE_UNDERLINE and cur_pieces[0][0] == SPIECE_UNDERLINE:
109 | if len(cur_pieces[0]) == 1:
110 | cur_pieces = cur_pieces[1:]
111 | else:
112 | cur_pieces[0] = cur_pieces[0][1:]
113 | cur_pieces.append(piece[-1])
114 | new_pieces.extend(cur_pieces)
115 | else:
116 | new_pieces.append(piece)
117 |
118 | # note(zhiliny): convert back to unicode for py2
119 | if six.PY2 and return_unicode:
120 | ret_pieces = []
121 | for piece in new_pieces:
122 | if isinstance(piece, str):
123 | piece = six.ensure_text(piece, "utf-8")
124 | ret_pieces.append(piece)
125 | new_pieces = ret_pieces
126 |
127 | return new_pieces
128 |
129 |
130 | def encode_ids(sp_model, text, sample=False):
131 | pieces = encode_pieces(sp_model, text, return_unicode=False, sample=sample)
132 | ids = [sp_model.PieceToId(piece) for piece in pieces]
133 | return ids
134 |
135 |
136 | def convert_to_unicode(text):
137 | """Converts `text` to Unicode (if it's not already), assuming utf-8 input."""
138 | if six.PY3:
139 | if type(text) in [str,bytes]:
140 | if isinstance(text, str):
141 | return text
142 | elif isinstance(text, bytes):
143 | return six.ensure_text(text, "utf-8", "ignore")
144 | else:
145 | raise ValueError("Unsupported string type: %s" % (type(text)))
146 | # by chenming
147 | else:
148 | return text
149 | elif six.PY2:
150 | if isinstance(text, str):
151 | return six.ensure_text(text, "utf-8", "ignore")
152 | elif isinstance(text, six.text_type):
153 | return text
154 | else:
155 | raise ValueError("Unsupported string type: %s" % (type(text)))
156 | else:
157 | raise ValueError("Not running on Python2 or Python 3?")
158 |
159 |
160 |
161 | def printale_text(text):
162 | """Returnsb text encoded in a way suitable for print or `tf.logging`."""
163 |
164 | # These functions want `str` for both Python2 and Python3, but in one case
165 | # it's a Unicode string and in the other it's a byte string.
166 | if six.PY3:
167 | if isinstance(text, str):
168 | return text
169 | elif isinstance(text, bytes):
170 | return six.ensure_text(text, "utf-8", "ignore")
171 | else:
172 | raise ValueError("Unsupported string type: %s" % (type(text)))
173 | elif six.PY2:
174 | if isinstance(text, str):
175 | return text
176 | elif isinstance(text, six.text_type):
177 | return six.ensure_binary(text, "utf-8")
178 | else:
179 | raise ValueError("Unsupported string type: %s" % (type(text)))
180 | else:
181 | raise ValueError("Not running on Python2 or Python 3?")
182 |
183 |
184 | def load_vocab(vocab_file):
185 | """Loads a vocabulary file into a dictionary."""
186 | vocab = collections.OrderedDict()
187 | with tf.gfile.GFile(vocab_file, "r") as reader:
188 | while True:
189 | token = convert_to_unicode(reader.readline())
190 | if not token:
191 | break
192 | token = token.strip()#.split()[0]
193 | if token not in vocab:
194 | vocab[token] = len(vocab)
195 | return vocab
196 |
197 |
198 |
199 |
200 | def convert_by_vocab(vocab, items):
201 | """Converts a sequence of [tokens|ids] using the vocab."""
202 | output = []
203 | for item in items:
204 | output.append(vocab[item])
205 | return output
206 |
207 |
208 | def convert_tokens_to_ids(vocab, tokens):
209 | return convert_by_vocab(vocab, tokens)
210 |
211 |
212 | def convert_ids_to_tokens(inv_vocab, ids):
213 | return convert_by_vocab(inv_vocab, ids)
214 |
215 |
216 | def whitespace_tokenize(text):
217 | """Runs basic whitespace cleaning and splitting on a piece of text."""
218 | text = text.strip()
219 | if not text:
220 | return []
221 | tokens = text.split()
222 | return tokens
223 |
224 |
225 | class FullTokenizer(object):
226 | """Runs end-to-end tokenziation."""
227 |
228 | def __init__(self, vocab_file, do_lower_case=True, spm_model_file=None):
229 | self.vocab = None
230 | self.sp_model = None
231 | if spm_model_file:
232 | self.sp_model = spm.SentencePieceProcessor()
233 | tf.logging.info("loading sentence piece model")
234 | self.sp_model.Load(spm_model_file)
235 | # Note(mingdachen): For the purpose of consisent API, we are
236 | # generating a vocabulary for the sentence piece tokenizer.
237 | self.vocab = {self.sp_model.IdToPiece(i): i for i
238 | in range(self.sp_model.GetPieceSize())}
239 | else:
240 | self.vocab = load_vocab(vocab_file)
241 | self.basic_tokenizer = BasicTokenizer(do_lower_case=do_lower_case)
242 | self.wordpiece_tokenizer = WordpieceTokenizer(vocab=self.vocab)
243 | self.inv_vocab = {v: k for k, v in self.vocab.items()}
244 |
245 | @classmethod
246 | def from_scratch(cls, vocab_file, do_lower_case, spm_model_file):
247 | return FullTokenizer(vocab_file, do_lower_case, spm_model_file)
248 |
249 | # @classmethod
250 | # def from_hub_module(cls, hub_module, spm_model_file):
251 | # """Get the vocab file and casing info from the Hub module."""
252 | # with tf.Graph().as_default():
253 | # albert_module = hub.Module(hub_module)
254 | # tokenization_info = albert_module(signature="tokenization_info",
255 | # as_dict=True)
256 | # with tf.Session() as sess:
257 | # vocab_file, do_lower_case = sess.run(
258 | # [tokenization_info["vocab_file"],
259 | # tokenization_info["do_lower_case"]])
260 | # return FullTokenizer(
261 | # vocab_file=vocab_file, do_lower_case=do_lower_case,
262 | # spm_model_file=spm_model_file)
263 |
264 | def tokenize(self, text):
265 | if self.sp_model:
266 | split_tokens = encode_pieces(self.sp_model, text, return_unicode=False)
267 | else:
268 | split_tokens = []
269 | for token in self.basic_tokenizer.tokenize(text):
270 | for sub_token in self.wordpiece_tokenizer.tokenize(token):
271 | split_tokens.append(sub_token)
272 |
273 | return split_tokens
274 |
275 | def convert_tokens_to_ids(self, tokens):
276 | if self.sp_model:
277 | tf.logging.info("using sentence piece tokenzier.")
278 | return [self.sp_model.PieceToId(
279 | printable_text(token)) for token in tokens]
280 | else:
281 | return convert_by_vocab(self.vocab, tokens)
282 |
283 | def convert_ids_to_tokens(self, ids):
284 | if self.sp_model:
285 | tf.logging.info("using sentence piece tokenzier.")
286 | return [self.sp_model.IdToPiece(id_) for id_ in ids]
287 | else:
288 | return convert_by_vocab(self.inv_vocab, ids)
289 |
290 |
291 | class BasicTokenizer(object):
292 | """Runs basic tokenization (punctuation splitting, lower casing, etc.)."""
293 |
294 | def __init__(self, do_lower_case=True):
295 | """Constructs a BasicTokenizer.
296 |
297 | Args:
298 | do_lower_case: Whether to lower case the input.
299 | """
300 | self.do_lower_case = do_lower_case
301 |
302 | def tokenize(self, text):
303 | """Tokenizes a piece of text."""
304 | text = convert_to_unicode(text)
305 | text = self._clean_text(text)
306 |
307 | # This was added on November 1st, 2018 for the multilingual and Chinese
308 | # models. This is also applied to the English models now, but it doesn't
309 | # matter since the English models were not trained on any Chinese data
310 | # and generally don't have any Chinese data in them (there are Chinese
311 | # characters in the vocabulary because Wikipedia does have some Chinese
312 | # words in the English Wikipedia.).
313 | text = self._tokenize_chinese_chars(text)
314 |
315 | orig_tokens = whitespace_tokenize(text)
316 | split_tokens = []
317 | for token in orig_tokens:
318 | if self.do_lower_case:
319 | token = token.lower()
320 | token = self._run_strip_accents(token)
321 | split_tokens.extend(self._run_split_on_punc(token))
322 |
323 | output_tokens = whitespace_tokenize(" ".join(split_tokens))
324 | return output_tokens
325 |
326 | def _run_strip_accents(self, text):
327 | """Strips accents from a piece of text."""
328 | text = unicodedata.normalize("NFD", text)
329 | output = []
330 | for char in text:
331 | cat = unicodedata.category(char)
332 | if cat == "Mn":
333 | continue
334 | output.append(char)
335 | return "".join(output)
336 |
337 | def _run_split_on_punc(self, text):
338 | """Splits punctuation on a piece of text."""
339 | chars = list(text)
340 | i = 0
341 | start_new_word = True
342 | output = []
343 | while i < len(chars):
344 | char = chars[i]
345 | if _is_punctuation(char):
346 | output.append([char])
347 | start_new_word = True
348 | else:
349 | if start_new_word:
350 | output.append([])
351 | start_new_word = False
352 | output[-1].append(char)
353 | i += 1
354 |
355 | return ["".join(x) for x in output]
356 |
357 | def _tokenize_chinese_chars(self, text):
358 | """Adds whitespace around any CJK character."""
359 | output = []
360 | for char in text:
361 | cp = ord(char)
362 | if self._is_chinese_char(cp):
363 | output.append(" ")
364 | output.append(char)
365 | output.append(" ")
366 | else:
367 | output.append(char)
368 | return "".join(output)
369 |
370 | def _is_chinese_char(self, cp):
371 | """Checks whether CP is the codepoint of a CJK character."""
372 | # This defines a "chinese character" as anything in the CJK Unicode block:
373 | # https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block)
374 | #
375 | # Note that the CJK Unicode block is NOT all Japanese and Korean characters,
376 | # despite its name. The modern Korean Hangul alphabet is a different block,
377 | # as is Japanese Hiragana and Katakana. Those alphabets are used to write
378 | # space-separated words, so they are not treated specially and handled
379 | # like the all of the other languages.
380 | if ((cp >= 0x4E00 and cp <= 0x9FFF) or #
381 | (cp >= 0x3400 and cp <= 0x4DBF) or #
382 | (cp >= 0x20000 and cp <= 0x2A6DF) or #
383 | (cp >= 0x2A700 and cp <= 0x2B73F) or #
384 | (cp >= 0x2B740 and cp <= 0x2B81F) or #
385 | (cp >= 0x2B820 and cp <= 0x2CEAF) or
386 | (cp >= 0xF900 and cp <= 0xFAFF) or #
387 | (cp >= 0x2F800 and cp <= 0x2FA1F)): #
388 | return True
389 |
390 | return False
391 |
392 | def _clean_text(self, text):
393 | """Performs invalid character removal and whitespace cleanup on text."""
394 | output = []
395 | for char in text:
396 | cp = ord(char)
397 | if cp == 0 or cp == 0xfffd or _is_control(char):
398 | continue
399 | if _is_whitespace(char):
400 | output.append(" ")
401 | else:
402 | output.append(char)
403 | return "".join(output)
404 |
405 |
406 | class WordpieceTokenizer(object):
407 | """Runs WordPiece tokenziation."""
408 |
409 | def __init__(self, vocab, unk_token="[UNK]", max_input_chars_per_word=200):
410 | self.vocab = vocab
411 | self.unk_token = unk_token
412 | self.max_input_chars_per_word = max_input_chars_per_word
413 |
414 | def tokenize(self, text):
415 | """Tokenizes a piece of text into its word pieces.
416 |
417 | This uses a greedy longest-match-first algorithm to perform tokenization
418 | using the given vocabulary.
419 |
420 | For example:
421 | input = "unaffable"
422 | output = ["un", "##aff", "##able"]
423 |
424 | Args:
425 | text: A single token or whitespace separated tokens. This should have
426 | already been passed through `BasicTokenizer.
427 |
428 | Returns:
429 | A list of wordpiece tokens.
430 | """
431 |
432 | text = convert_to_unicode(text)
433 |
434 | output_tokens = []
435 | for token in whitespace_tokenize(text):
436 | chars = list(token)
437 | if len(chars) > self.max_input_chars_per_word:
438 | output_tokens.append(self.unk_token)
439 | continue
440 |
441 | is_bad = False
442 | start = 0
443 | sub_tokens = []
444 | while start < len(chars):
445 | end = len(chars)
446 | cur_substr = None
447 | while start < end:
448 | substr = "".join(chars[start:end])
449 | if start > 0:
450 | substr = "##" + six.ensure_str(substr)
451 | if substr in self.vocab:
452 | cur_substr = substr
453 | break
454 | end -= 1
455 | if cur_substr is None:
456 | is_bad = True
457 | break
458 | sub_tokens.append(cur_substr)
459 | start = end
460 |
461 | if is_bad:
462 | output_tokens.append(self.unk_token)
463 | else:
464 | output_tokens.extend(sub_tokens)
465 | return output_tokens
466 |
467 |
468 | def _is_whitespace(char):
469 | """Checks whether `chars` is a whitespace character."""
470 | # \t, \n, and \r are technically control characters but we treat them
471 | # as whitespace since they are generally considered as such.
472 | if char == " " or char == "\t" or char == "\n" or char == "\r":
473 | return True
474 | cat = unicodedata.category(char)
475 | if cat == "Zs":
476 | return True
477 | return False
478 |
479 |
480 | def _is_control(char):
481 | """Checks whether `chars` is a control character."""
482 | # These are technically control characters but we count them as whitespace
483 | # characters.
484 | if char == "\t" or char == "\n" or char == "\r":
485 | return False
486 | cat = unicodedata.category(char)
487 | if cat in ("Cc", "Cf"):
488 | return True
489 | return False
490 |
491 |
492 | def _is_punctuation(char):
493 | """Checks whether `chars` is a punctuation character."""
494 | cp = ord(char)
495 | # We treat all non-letter/number ASCII as punctuation.
496 | # Characters such as "^", "$", and "`" are not in the Unicode
497 | # Punctuation class but we treat them as punctuation anyways, for
498 | # consistency.
499 | if ((cp >= 33 and cp <= 47) or (cp >= 58 and cp <= 64) or
500 | (cp >= 91 and cp <= 96) or (cp >= 123 and cp <= 126)):
501 | return True
502 | cat = unicodedata.category(char)
503 | if cat.startswith("P"):
504 | return True
505 | return False
506 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/train.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu May 30 21:42:07 2021
4 |
5 | @author: cm
6 | """
7 |
8 | import os
9 | #os.environ["CUDA_VISIBLE_DEVICES"] = '-1'
10 | import numpy as np
11 | import tensorflow as tf
12 | from classifier_multi_label_denses.networks import NetworkAlbert
13 | from classifier_multi_label_denses.classifier_utils import get_features
14 | from classifier_multi_label_denses.hyperparameters import Hyperparamters as hp
15 | from classifier_multi_label_denses.utils import select,time_now_string
16 |
17 |
18 |
19 | pwd = os.path.dirname(os.path.abspath(__file__))
20 | MODEL = NetworkAlbert(is_training=True)
21 |
22 |
23 |
24 |
25 | # Get data features
26 | input_ids,input_masks,segment_ids,label_ids = get_features()
27 | num_train_samples = len(input_ids)
28 | indexs = np.arange(num_train_samples)
29 | num_batchs = int((num_train_samples - 1) /hp.batch_size) + 1
30 | print('Number of batch:',num_batchs)
31 |
32 | # Set up the graph
33 | saver = tf.train.Saver(max_to_keep=hp.max_to_keep)
34 | sess = tf.Session()
35 | sess.run(tf.global_variables_initializer())
36 |
37 | # Load model saved before
38 | MODEL_SAVE_PATH = os.path.join(pwd, hp.file_model_save)
39 | ckpt = tf.train.get_checkpoint_state(MODEL_SAVE_PATH)
40 | if ckpt and ckpt.model_checkpoint_path:
41 | saver.restore(sess, ckpt.model_checkpoint_path)
42 | print('Restored model!')
43 |
44 |
45 | with sess.as_default():
46 | # Tensorboard writer
47 | writer = tf.summary.FileWriter(hp.logdir, sess.graph)
48 | for i in range(hp.num_train_epochs):
49 | np.random.shuffle(indexs)
50 | for j in range(num_batchs-1):
51 | # Get ids selected
52 | i1 = indexs[j * hp.batch_size:min((j + 1) * hp.batch_size, num_train_samples)]
53 |
54 | # Get features
55 | input_id_ = select(input_ids,i1)
56 | input_mask_ = select(input_masks,i1)
57 | segment_id_ = select(segment_ids,i1)
58 | label_id_ = select(label_ids,i1)
59 |
60 | # Feed dict
61 | fd = {MODEL.input_ids: input_id_,
62 | MODEL.input_masks: input_mask_,
63 | MODEL.segment_ids:segment_id_,
64 | MODEL.label_ids:label_id_}
65 |
66 | # Optimizer
67 | sess.run(MODEL.optimizer, feed_dict = fd)
68 |
69 | # Tensorboard
70 | if j%hp.summary_step==0:
71 | summary,glolal_step = sess.run([MODEL.merged,MODEL.global_step], feed_dict = fd)
72 | writer.add_summary(summary, glolal_step)
73 |
74 | # Save Model
75 | if j%(num_batchs//hp.num_saved_per_epoch)==0:
76 | if not os.path.exists(os.path.join(pwd, hp.file_model_save)):
77 | os.makedirs(os.path.join(pwd, hp.file_model_save))
78 | saver.save(sess, os.path.join(pwd, hp.file_model_save, 'model'+'_%s_%s.ckpt'%(str(i),str(j))))
79 |
80 | # Log
81 | if j % hp.print_step == 0:
82 | fd = {MODEL.input_ids: input_id_,
83 | MODEL.input_masks: input_mask_,
84 | MODEL.segment_ids:segment_id_,
85 | MODEL.label_ids:label_id_}
86 | loss = sess.run(MODEL.loss, feed_dict = fd)
87 | print('Time:%s, Epoch:%s, Batch number:%s/%s, Loss:%s'%(time_now_string(),str(i),str(j),str(num_batchs),str(loss)))
88 | print('Train finished')
89 |
90 |
91 |
--------------------------------------------------------------------------------
/classifier_multi_label_denses/utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Fri May 25 23:43:39 2021
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import time
10 | import numpy as np
11 | import pandas as pd
12 |
13 |
14 | def time_now_string():
15 | return time.strftime("%Y-%m-%d %H:%M:%S",time.localtime( time.time() ))
16 |
17 |
18 | def select(data,ids):
19 | return [data[i] for i in ids]
20 |
21 |
22 | def load_txt(file):
23 | with open(file,encoding='utf-8',errors='ignore') as fp:
24 | lines = fp.readlines()
25 | lines = [l.strip() for l in lines]
26 | print("Load data from file (%s) finished !"%file)
27 | return lines
28 |
29 |
30 | def save_txt(file,lines):
31 | lines = [l+'\n' for l in lines]
32 | with open(file,'w+',encoding='utf-8') as fp:
33 | fp.writelines(lines)
34 | return "Write data to txt finished !"
35 |
36 |
37 | def load_csv(file,encoding='utf-8',header=0):
38 | return pd.read_csv(file,
39 | encoding=encoding,
40 | header=header,
41 | error_bad_lines=False)
42 |
43 |
44 | def save_csv(dataframe,file,header=True,index=None,encoding="gbk"):
45 | return dataframe.to_csv(file,
46 | mode='w+',
47 | header=header,
48 | index=index,
49 | encoding=encoding)
50 |
51 |
52 |
53 | def save_excel(dataframe,file,header=True,sheetname='Sheet1'):
54 | return dataframe.to_excel(file,
55 | header=header,
56 | sheet_name=sheetname)
57 |
58 |
59 | def load_excel(file,header=0):
60 | return pd.read_excel(file,
61 | header=header,
62 | )
63 |
64 | def load_vocabulary(file_vocabulary_label):
65 | """
66 | Load vocabulary to dict
67 | """
68 | vocabulary = load_txt(file_vocabulary_label)
69 | dict_id2label,dict_label2id = {},{}
70 | for i,l in enumerate(vocabulary):
71 | dict_id2label[str(i)] = str(l)
72 | dict_label2id[str(l)] = str(i)
73 | return dict_id2label,dict_label2id
74 |
75 |
76 | def shuffle_two(a1,a2):
77 | ran = np.arange(len(a1))
78 | np.random.shuffle(ran)
79 | a1_ = [a1[l] for l in ran]
80 | a2_ = [a2[l] for l in ran]
81 | return a1_, a2_
82 |
83 |
84 | if __name__ == '__main__':
85 | print('')
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/README.md:
--------------------------------------------------------------------------------
1 | # 简介
2 | 1、本项目是在tensorflow版本1.15.0的基础上做的训练和测试。
3 | 2、本项目为中文的多标签文本分类。
4 | 3、欢迎大家联系我 www.hellonlp.com
5 | 4、albert_small_zh_google对应的百度云下载地址:
6 | 链接:https://pan.baidu.com/s/1RKzGJTazlZ7y12YRbAWvyA
7 | 提取码:wuxw
8 |
9 | # 使用方法
10 | 1、准备数据
11 | 数据格式为:classifier_multi_label_seq2seq_attention/data/test.csv
12 | 2、参数设置
13 | 参考脚本 hyperparameters.py,直接修改里面的数值即可。
14 | 3、训练
15 | ```
16 | python train.py
17 | ```
18 | 4、预测
19 | ```
20 | python predict.py
21 | ```
22 | 注意:推理时需要把model/save中的模型复制到model/load中,并修改model/load中的checkpoint文件的内容为当前模型名称,例如:model_checkpoint_path: "model_xx_xx.ckpt"。
23 | 。
24 |
25 |
26 | # 知乎解读
27 | https://zhuanlan.zhihu.com/p/260743336
28 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/__pycache__/classifier_utils.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_seq2seq/__pycache__/classifier_utils.cpython-37.pyc
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/__pycache__/hyperparameters.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_seq2seq/__pycache__/hyperparameters.cpython-37.pyc
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/__pycache__/lamb_optimizer.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_seq2seq/__pycache__/lamb_optimizer.cpython-37.pyc
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/__pycache__/load.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_seq2seq/__pycache__/load.cpython-37.pyc
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/__pycache__/modeling.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_seq2seq/__pycache__/modeling.cpython-37.pyc
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/__pycache__/modules.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_seq2seq/__pycache__/modules.cpython-37.pyc
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/__pycache__/networks.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_seq2seq/__pycache__/networks.cpython-37.pyc
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/__pycache__/optimization.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_seq2seq/__pycache__/optimization.cpython-37.pyc
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/__pycache__/predict.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_seq2seq/__pycache__/predict.cpython-37.pyc
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/__pycache__/tokenization.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_seq2seq/__pycache__/tokenization.cpython-37.pyc
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/__pycache__/utils.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_seq2seq/__pycache__/utils.cpython-37.pyc
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/albert_small_zh_google/checkpoint:
--------------------------------------------------------------------------------
1 | model_checkpoint_path: "albert_model.ckpt"
2 | all_model_checkpoint_paths: "albert_model.ckpt"
3 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/data/test.csv:
--------------------------------------------------------------------------------
1 | content,label
2 | 这肯定是个假店,
3 | 我是把他们寄给我的所有东西算寄给他们了,
4 | 也就偶尔做个表,
5 | 运行速度:开机不到十秒钟,
6 | 散热性能:散热真垃圾,
7 | 就是系统不怎么好用,
8 | 不会自动启动,
9 | 客服小可的服务也很好,
10 | 否则开机3分钟就发热了,
11 | 充电时完全不能用,产品整体评价
12 | 不能差太多,
13 | 而且我用的是华为手机,
14 | 发货的时候漏发鼠标,
15 | 办公用运行速度还可以,
16 | 触摸屏很省力,
17 | 电脑风扇确实不是太静音,
18 | 没想到真挺好用,产品整体评价
19 | 建议直接送小米游戏耳机,
20 | 可以说这有点霸王条款玩的文字游戏,
21 | 不知道后期会不会再出现,
22 | 不支持Windows系统,
23 | 所以又买了戴尔,
24 | 焚香,
25 | 京东快递还在坚持送货,
26 | 单位够买,
27 | 其他特色:掀开盖子自动开机,
28 | 可以玩LOL没有问题,
29 | 游戏本很可以啦,产品整体评价
30 | 先用试试水哦嘻嘻,
31 | 驱动精灵不得,
32 | 所以最好是装好系统激活了再发货,
33 | 除摄像头位置不好,
34 | 劝告各位 如果是买来作为游戏机,
35 | 所以买了华为电脑,
36 | 我觉得这不是正版,
37 | 物流也是超级给力 赞👍,
38 | 还是感觉电脑操作一般,产品整体评价
39 | 找他们也没用,
40 | 上身效果会好看一些,
41 | 网络就连接不上,
42 | 简约外观,外观设计
43 | 就转圈圈,
44 | 所以就优先考虑买华为的笔记本,
45 | 其他特色:手纹快速开机又安全,
46 | 就是我手机热点要感应很久才可以,
47 | 散热性能:暂时发热不明显,
48 | logo周围的槽打磨不平整,外壳做工
49 | 大爱必须五星呀,产品整体评价
50 | 外形外观:机器外观不错,外观设计
51 | 运行速度秒级别快,
52 | 一切满意,
53 | 从手机型号4-8,
54 | 我这是二手货,
55 | 总体就是不好,产品整体评价
56 | 包括后来咨询补发是也很好,
57 | 快递效率也高,
58 | 外观材质:我买的亮银色的超级好看,机身材质/机身颜色
59 | 华为笔记本配华为手机,
60 | 用的时间长了风扇声音会有点明显,
61 | 电脑排风扇声音太大,
62 | 因为后盖有点翘着,外壳做工
63 | 这个电脑收到两天了,
64 | 画面品质:还没玩游戏应该还可以吧,
65 | 欢迎大家花钱买真货,
66 | 屏幕效果:屏幕看起来很细腻,
67 | 平常状态应该好一些,产品整体评价
68 | 客服潇洒也真的是潇洒😂,
69 | 优点:屏幕占比大,
70 | 轻薄程度:很轻薄很漂亮,重量尺寸
71 | 虽说有时候风扇有点响,
72 | 就是电量只能支撑4个多小时的视频,
73 | 外形外观:外观整体银灰色非常美观,机身颜色
74 | 纠结了好久才决定买银蓝色,
75 | 我开着ps和ai都不卡,
76 | 但就是店家发货太慢了啊啊啊啊,
77 | 电脑卡慢是怎么回事,
78 | 用着特别快,
79 | 整体来说电脑除了灵敏度不行其他都可以,产品整体评价
80 | 轻薄本的通病,重量尺寸
81 | 无语了有点后悔,
82 | 粉色少女心爆棚,机身颜色
83 | 值得购买的,产品整体评价
84 | 以后再碰戴尔的东西我**,
85 | 连基本的办公软件用着也卡,
86 | 用了几天电脑质量还不错,产品整体评价
87 | 显卡效果:超级流畅,
88 | 现在商家真是扣到家了,
89 | 声音也响,
90 | 低调蓝,机身颜色
91 | 送了安装纯净版windows的u盘,
92 | 才用10,
93 | 运行速度:这个价位,
94 | 可以和华为手机共享,
95 | 期待了很久终于到手了,
96 | 挺好的搭配华为手机使用更加👍,
97 | 也真正实现了在手机和笔记本之间的投屏,
98 | 我手机用的是华为P9到P30pro一直忠于他的性能与质量,
99 | 指纹解锁很方便除了电源,
100 | 唯一让我犹豫了一下的是,
101 | 公司批量采购的笔记本,
102 | 运行时基本没声音,
103 | 比预期的快了两天,
104 | 快递员送货立马就说了,
105 | 外形外观:5分,外观设计
106 | 屏幕的彩色非常鲜艳,
107 | 要不左边真的烫,
108 | 日常办公足矣,产品整体评价
109 | 不知道是不是兼容性不太好,
110 | 频繁出现我才要求换货的,
111 | 好用 🉑️ 真的不错,产品整体评价
112 | 非常有质感,机身材质
113 | 偶尔会想几下风扇的声音噪音很小,
114 | 驱动升级,
115 | 客服真心不错,
116 | 屏幕看上去也很舒服,
117 | 会一直支持华为做大做强👍,
118 | 挺好看的 就是激活windows网一直出错 半天才搞好,外观设计
119 | 就是没有送包感觉有点不好,
120 | 客服态度也还蛮好,
121 | 还要一会儿,
122 | 隐蔽式摄像头很创新,
123 | 买给妹妹当学习本的,
124 | 外形外观:外形设计有心了,外观设计
125 | 有谁见过这样的电脑 真的垃圾,产品整体评价
126 | 第一台10年了,
127 | 我都尽量给孩子们严格把关,
128 | 孩子说比原来那个好,产品整体评价
129 | 之前一直也有点担心,
130 | 触屏功能很新颖,
131 | 学会了如何分盘,产品整体评价
132 | 集显完全够用了,
133 | 绑定小米真的非常方便,
134 | 所以果断换了这家店,
135 | 我主要是办公软件还有PS,
136 | 各方面非常到位,产品整体评价
137 | 所以买了的兄弟姐妹就自求多福吧,产品整体评价
138 | 刚开机不久只是注册了一下就开始发热了,
139 | 我是为了无线鼠标才来评论的,
140 | 自己选择很久了,
141 | 电脑总体来说还是满意的,产品整体评价
142 | 主要是买的东西太多,
143 | 也没有出现你说的那种情况啊,产品整体评价
144 | 散热器声音响起来确实挺大,
145 | 怀疑他们家是不是只有一个客服,
146 | 打游戏看着要舒服很多,
147 | 后续再评价性能感受,
148 | 买来就是做做课件,
149 | 买了个绿联的扩展坞,
150 | 刚用不好评论,
151 | 劝大家谨慎购买吧,产品整体评价
152 | 为什么我的跑分怎么低,
153 | 有前置摄像头,
154 | 就是价位高了点,
155 | 一点都不输苹果呀,
156 | 应该是1920*1080P的分辨率,
157 | 轻薄程度:我觉得可以接受,重量尺寸
158 | 重量大小出差也很OK,重量尺寸
159 | 拿起来好轻,重量尺寸
160 | 图一和图二是一个颜色,机身颜色
161 | 屏幕效果:分辨率很高的,
162 | 电脑整体做工还可以,
163 | 感谢为我补发的鼠标,
164 | 最终定位华为品牌,
165 | 三星512G M2高速硬盘,
166 | 同行的话不会买这个啦,产品整体评价
167 | 本来担心几千块东西走物流行不行,
168 | 而且色域不错,
169 | 一天用个几分钟,
170 | 1、开箱后感觉外形做工非常漂亮,外观设计
171 | 虽然有让我产生一些不愉快,
172 | 考虑入手的大家还是换一家吧,产品整体评价
173 | 选择,
174 | cpu基本够用,
175 | 之前那台Thinkpad很老旧了,
176 | 自然我喜欢它就有它的特点,产品整体评价
177 | 运行速度:运行速度:换了Windows系统(换系统各种问题,
178 | 有电源时候,
179 | 买完第二天就降价200,
180 | 买个性能强的笔记本主要为了敲代码,
181 | 出门带着也很方便客服服务态度也非常好很耐心的解答,重量尺寸
182 | 色彩很鲜艳真实,
183 | 女孩子选电脑好看就行了,外观设计
184 | 开机也有点慢,
185 | 另外再告诉大家一个问题,
186 | 非常高清的画面,
187 | 缺少的东西也安排了补发,
188 | 屏幕效果:打开之后特别清晰,
189 | 你的人生态度就是这样一件事情就是这样一件小事就可以了,
190 | 电源键都有问题,
191 | 屏幕效果:清晰度也可以,
192 | 没有鼠标操作有点不方便,
193 | 秒回消息,
194 | 散热性能:暂未高强度使用,
195 | 出乎意料的散热差,
196 | 还,
197 | 感觉跟4千多的没什么区别,
198 | 我的反正是起飞了,
199 | 一个下午出现2次电脑卡死,
200 | 华为电脑做工很不错,
201 | 其他的物流方面,
202 | 还想买一台,
203 | 就是是从武汉发过来的,
204 | 已经给小孩了,
205 | 这个是语音操作设置,
206 | 但感觉不是很轻,重量尺寸
207 | 菜单什么都点不开,
208 | 没有特别值得夸奖,产品整体评价
209 | 没有想象中特别高清,
210 | 办公不会再卡啦,
211 | 外观好看简约大方,外观设计
212 | 让笔记本显得很小巧,重量尺寸
213 | 感觉目前对于我这种大学生来说相当适合,产品整体评价
214 | 打电话想求助,
215 | 可是角度大 性能也够用,
216 | 台式准备卖了,
217 | 觉得Dell很不错,
218 | 容易**,
219 | 不错很好用起来很方便宜点的没有找到,产品整体评价
220 | 512GB PCIe SSD,
221 | 小米的还是不行,
222 | 找苏宁易购的客服解决,
223 | 拿到电脑用了一周多了,
224 | 一直使用联想笔记本、上一个用了10年,
225 | 没送鼠标就算了吧,
226 | 不重操作还算简单,重量尺寸/产品整体评价
227 | 后面收到货后说要退货又给重新发了一个,
228 | 那个包没带子不能背,
229 | 买几千元的东西用一个月,
230 | 好歹几千块的东西,
231 | 不得不说京东服务不错,
232 | 明白吧,
233 | 无需输入密码,
234 | 散热性能:还没有玩过游戏,
235 | 联系工程师说硬件问题,
236 | 特别是电脑背面的大logo以及页面介绍的那个排风系统的外观,外观设计
237 | 联想小新pro13酷睿系列真的,
238 | 这是第一次在京东买东西,
239 | 画面品质:看起来很舒服,
240 | 京东买东西就是好,
241 | 我价保一过,
242 | 然后觉得声音有点大,
243 | 包装也只是加了个塑料袋,
244 | 很值呀,
245 | 而且占屏率高,
246 | 这样用起来真的很不方便,产品整体评价
247 | 外形外观:我选的皓月银很大气,机身颜色
248 | 如后续仍问题不断,
249 | 这个价格相当实惠,
250 | 难道是C盘东西多了吗,
251 | 关注了很多天,
252 | 不建议玩游戏有点卡,
253 | 看起来就是舒服,产品整体评价
254 | 充着这个品牌去,
255 | 居然一个鼠标都没有,
256 | 评论完了告诉我要加3张图才能领E卡,
257 | 简直严重差评,产品整体评价
258 | 也非常灵敏,
259 | 有点提心吊胆,
260 | 真烦人,
261 | 可以补回来,
262 | 运行速度:性能好,
263 | 怪自己买的急了,
264 | 已经到了两三天了,
265 | 比较认可,产品整体评价
266 | 运行cad好快,
267 | 就是玩游戏有点小卡,
268 | 卖家也很好👍👍👍,
269 | 唯一的吐槽点就是没USB接口要额外转接器比较麻烦点,
270 | 鼠标不能一起送货吗,
271 | 超级喜欢奥,产品整体评价
272 | 重启才会好,
273 | 感觉电一下就没有了,
274 | 公司通知在家办公毫不犹豫下单了,
275 | 办公外出携带完美,重量尺寸
276 | 运行速度:速度点击很快,
277 | 也按客服说的又是修复又是卸载又是重装,
278 | 期待已久了,
279 | office 无法注册使用,
280 | 并且还有滋滋的声响,
281 | 所谓的性能猛兽完全就是骗人的,
282 | 太爱这个电脑了,产品整体评价
283 | 第一次在天猫旗舰店购买,
284 | 网络顺畅,
285 | 系统在关机和开机的时候都自动更新系统,
286 | 轻薄程度:比预想的轻多了,重量尺寸
287 | 运行速度:直接慢,
288 | 在直播间下单的,
289 | 发现使用两三天以后就开始卡了,
290 | 按我要求,
291 | 运行速度:16g内存保证了运行流畅,
292 | 指纹等功能太强大了,
293 | 放音乐乐质也不错,
294 | 与台式机比起来反应略迟钝,
295 | 风机开起来声音好大,
296 | 散热性能:也就那样,
297 | 外形外观:小新风格带指纹,外观设计
298 | 官方客服电话打不通,
299 | 简直突破我的想象,产品整体评价
300 | 我当时也是没货客服说核对就上了一个,
301 | 相信京东的品控才在京东买电子产品的,
302 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/data/vocabulary_label.txt:
--------------------------------------------------------------------------------
1 | S
2 | E
3 | |
4 | 产品整体评价
5 | 外观设计
6 | 机身颜色
7 | 重量尺寸
8 | 机身材质
9 | 外壳做工
10 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/eval.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu Apr 9 10:56:43 2021
4 |
5 | @author: cm
6 | """
7 |
8 |
9 |
10 | # 参考: https://github.com/hellonlp/classifier-multi-label/blob/master/evaluation/eval_multi_label.py
11 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/hyperparameters.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Mon Nov 12 14:23:12 2018
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import os
10 | import sys
11 | pwd = os.path.dirname(os.path.abspath(__file__))
12 | sys.path.append(pwd)
13 | from classifier_multi_label_seq2seq.utils import load_vocabulary
14 |
15 |
16 | class Hyperparamters:
17 | # Train parameters
18 | num_train_epochs = 20
19 | print_step = 100
20 | batch_size = 4 # 128
21 | summary_step = 10
22 | num_saved_per_epoch = 3
23 | max_to_keep = 100
24 | logdir = 'logdir/CML_Seq2Seq'
25 |
26 | # Model paths
27 | file_model_save = 'model/model_save'
28 | file_model_load = 'model/model_load'
29 |
30 | # Train/Test data
31 | data_dir = os.path.join(pwd, 'data')
32 | train_data = 'train.csv'
33 | test_data = 'test.csv'
34 |
35 | # Load vocabulcary dict
36 | dict_id2label, dict_label2id = load_vocabulary(os.path.join(pwd, 'data', 'vocabulary_label.txt'))
37 | label_vocabulary = list(dict_id2label.values())
38 |
39 | # Optimization parameters
40 | warmup_proportion = 0.1
41 | use_tpu = None
42 | do_lower_case = True
43 | learning_rate = 5e-5
44 |
45 | # BiLSTM parameters
46 | num_layer_lstm_encode = 3
47 | lstm_hidden_size = 768
48 | decoder_embedding_size = 768
49 |
50 | # Beam search
51 | is_beam_search = True
52 | beam_size = 5
53 | max_length = 5
54 |
55 | # Sequence and Label
56 | sequence_length = 60
57 | num_labels = len(list(dict_id2label))
58 |
59 | # ALBERT
60 | model = 'albert_small_zh_google'
61 | bert_path = os.path.join(pwd, model)
62 | vocab_file = os.path.join(pwd, model, 'vocab_chinese.txt')
63 | init_checkpoint = os.path.join(pwd, model, 'albert_model.ckpt')
64 | saved_model_path = os.path.join(pwd, 'model')
65 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/lamb_optimizer.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 |
3 |
4 | import re
5 | import six
6 | import tensorflow.compat.v1 as tf
7 | from tensorflow.python.ops import array_ops
8 | from tensorflow.python.ops import linalg_ops
9 | from tensorflow.python.ops import math_ops
10 |
11 |
12 | class LAMBOptimizer(tf.train.Optimizer):
13 | """LAMB (Layer-wise Adaptive Moments optimizer for Batch training)."""
14 |
15 | # A new optimizer that includes correct L2 weight decay, adaptive
16 | # element-wise updating, and layer-wise justification. The LAMB optimizer
17 | # was proposed by Yang You, Jing Li, Jonathan Hseu, Xiaodan Song,
18 | # James Demmel, and Cho-Jui Hsieh in a paper titled as Reducing BERT
19 | # Pre-Training Time from 3 Days to 76 Minutes (arxiv.org/abs/1904.00962)
20 |
21 | def __init__(self,
22 | learning_rate,
23 | weight_decay_rate=0.0,
24 | beta_1=0.9,
25 | beta_2=0.999,
26 | epsilon=1e-6,
27 | exclude_from_weight_decay=None,
28 | exclude_from_layer_adaptation=None,
29 | name="LAMBOptimizer"):
30 | """Constructs a LAMBOptimizer."""
31 | super(LAMBOptimizer, self).__init__(False, name)
32 |
33 | self.learning_rate = learning_rate
34 | self.weight_decay_rate = weight_decay_rate
35 | self.beta_1 = beta_1
36 | self.beta_2 = beta_2
37 | self.epsilon = epsilon
38 | self.exclude_from_weight_decay = exclude_from_weight_decay
39 | # exclude_from_layer_adaptation is set to exclude_from_weight_decay if the
40 | # arg is None.
41 | # TODO(jingli): validate if exclude_from_layer_adaptation is necessary.
42 | if exclude_from_layer_adaptation:
43 | self.exclude_from_layer_adaptation = exclude_from_layer_adaptation
44 | else:
45 | self.exclude_from_layer_adaptation = exclude_from_weight_decay
46 |
47 | def apply_gradients(self, grads_and_vars, global_step=None, name=None):
48 | """See base class."""
49 | assignments = []
50 | for (grad, param) in grads_and_vars:
51 | if grad is None or param is None:
52 | continue
53 |
54 | param_name = self._get_variable_name(param.name)
55 |
56 | m = tf.get_variable(
57 | name=six.ensure_str(param_name) + "/adam_m",
58 | shape=param.shape.as_list(),
59 | dtype=tf.float32,
60 | trainable=False,
61 | initializer=tf.zeros_initializer())
62 | v = tf.get_variable(
63 | name=six.ensure_str(param_name) + "/adam_v",
64 | shape=param.shape.as_list(),
65 | dtype=tf.float32,
66 | trainable=False,
67 | initializer=tf.zeros_initializer())
68 |
69 | # Standard Adam update.
70 | next_m = (
71 | tf.multiply(self.beta_1, m) + tf.multiply(1.0 - self.beta_1, grad))
72 | next_v = (
73 | tf.multiply(self.beta_2, v) + tf.multiply(1.0 - self.beta_2,
74 | tf.square(grad)))
75 |
76 | update = next_m / (tf.sqrt(next_v) + self.epsilon)
77 |
78 | # Just adding the square of the weights to the loss function is *not*
79 | # the correct way of using L2 regularization/weight decay with Adam,
80 | # since that will interact with the m and v parameters in strange ways.
81 | #
82 | # Instead we want ot decay the weights in a manner that doesn't interact
83 | # with the m/v parameters. This is equivalent to adding the square
84 | # of the weights to the loss with plain (non-momentum) SGD.
85 | if self._do_use_weight_decay(param_name):
86 | update += self.weight_decay_rate * param
87 |
88 | ratio = 1.0
89 | if self._do_layer_adaptation(param_name):
90 | w_norm = linalg_ops.norm(param, ord=2)
91 | g_norm = linalg_ops.norm(update, ord=2)
92 | ratio = array_ops.where(math_ops.greater(w_norm, 0), array_ops.where(
93 | math_ops.greater(g_norm, 0), (w_norm / g_norm), 1.0), 1.0)
94 |
95 | update_with_lr = ratio * self.learning_rate * update
96 |
97 | next_param = param - update_with_lr
98 |
99 | assignments.extend(
100 | [param.assign(next_param),
101 | m.assign(next_m),
102 | v.assign(next_v)])
103 | return tf.group(*assignments, name=name)
104 |
105 | def _do_use_weight_decay(self, param_name):
106 | """Whether to use L2 weight decay for `param_name`."""
107 | if not self.weight_decay_rate:
108 | return False
109 | if self.exclude_from_weight_decay:
110 | for r in self.exclude_from_weight_decay:
111 | if re.search(r, param_name) is not None:
112 | return False
113 | return True
114 |
115 | def _do_layer_adaptation(self, param_name):
116 | """Whether to do layer-wise learning rate adaptation for `param_name`."""
117 | if self.exclude_from_layer_adaptation:
118 | for r in self.exclude_from_layer_adaptation:
119 | if re.search(r, param_name) is not None:
120 | return False
121 | return True
122 |
123 | def _get_variable_name(self, param_name):
124 | """Get the variable name from the tensor name."""
125 | m = re.match("^(.*):\\d+$", six.ensure_str(param_name))
126 | if m is not None:
127 | param_name = m.group(1)
128 | return param_name
129 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/load.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Wed Jul 8 20:00:59 2020
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | from classifier_multi_label_seq2seq.hyperparameters import Hyperparamters as hp
10 |
11 |
12 | def label2onehot(string):
13 | string = '|' if string == '' else string
14 | string_list = list(str(string).split('/')) + ['E']
15 | return [int(hp.dict_label2id.get(l)) for l in string_list]
16 |
17 |
18 | def normalization_label(label_ids):
19 | max_length = max([len(l) for l in label_ids])
20 | return [l + [0] * (max_length - len(l)) if len(l) < max_length else l for i, l in enumerate(label_ids)]
21 |
22 |
23 | if __name__ == '__main__':
24 | # Test
25 | label_ids = [[1, 2, 3], [1, 2], [2, 3, 4, 5, 6]]
26 | print(normalization_label(label_ids))
27 | #
28 | # from classifier_multi_label_seq2seq.utils import load_csv,save_csv
29 | # f = 'classifier_multi_label_seq2seq/data/test.csv'
30 | # df = load_csv(f)
31 | # contents = df['content'].tolist()
32 | # labels = df['label'].tolist()
33 | # ls = ['产品整体评价','机身颜色','外观设计','重量尺寸','机身材质','外壳做工']
34 | # labels_new = []
35 | # for l in labels:
36 | # l1 = str(l).split('/')
37 | # l1_new = []
38 | # for li in l1:
39 | # if li in ls:
40 | # l1_new.append(li)
41 | # labels_new.append(l1_new)
42 | # #
43 | # import pandas as pd
44 | # df = pd.DataFrame(columns=['content','label'])
45 | # df['content'] = contents
46 | # df['label'] = ['/'.join(l) for l in labels_new]
47 | # file_csv = f = 'classifier_multi_label_seq2seq/data/test0.csv'
48 | # save_csv(df,file_csv,encoding='utf-8-sig')
49 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/logdir/CML_Seq2Seq/Readme.txt:
--------------------------------------------------------------------------------
1 | Readme
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/model/model_load/README.md:
--------------------------------------------------------------------------------
1 | # 推理所用的模型
2 |
3 | ## 模型路径
4 | - model_load/checkpoit
5 | - model_load/model_xx_0.ckpt.data-00000-of-00001
6 | - model_load/model_xx_0.ckpt.index
7 | - model_load/model_xx_0.ckpt.meta
8 |
9 | ## checkpoint内容
10 | ```
11 | model_checkpoint_path: "model_xx_0.ckpt"
12 | ```
13 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/model/model_save/README.md:
--------------------------------------------------------------------------------
1 | # 训练过程所得模型
2 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/modules.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Wed Jul 8 19:55:52 2020
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import tensorflow as tf
10 | from tensorflow.python.util import nest
11 | from classifier_multi_label_seq2seq.hyperparameters import Hyperparamters as hp
12 |
13 | dict_id2label, dict_label2id = hp.dict_id2label, hp.dict_label2id
14 |
15 |
16 | def shift_by_one(inputs):
17 | '''Shifts the content of `inputs` to the right by one
18 | so that it becomes the decoder inputs.
19 | Args:
20 | inputs: A 3d tensor with shape of [N, T, C]
21 | Returns:
22 | A 3d tensor with the same shape and dtype as `inputs`.
23 | '''
24 | return tf.concat((tf.zeros_like(inputs[:, :1]), inputs[:, :-1]), 1)
25 |
26 |
27 | def cell_lstm(lstm_hidden_size, is_training, scope='lstm', reuse=None):
28 | """
29 | A cell of LSTM
30 | """
31 | with tf.variable_scope(scope, reuse=reuse):
32 | lstm_cell = tf.contrib.rnn.BasicLSTMCell(lstm_hidden_size)
33 | lstm_cell_drop = tf.contrib.rnn.DropoutWrapper(cell=lstm_cell, output_keep_prob=0.5 if is_training else 1)
34 | return lstm_cell_drop
35 |
36 |
37 | def cell_attention_lstm(units, input_, _is_training):
38 | """
39 | A cell of attention
40 | """
41 | attention_mechanism = tf.contrib.seq2seq.BahdanauAttention(num_units=units,
42 | memory=input_)
43 | lstm_cell_ = tf.contrib.rnn.BasicLSTMCell(units)
44 | lstm_cell_drop = tf.contrib.rnn.DropoutWrapper(cell=lstm_cell_,
45 | output_keep_prob=0.5 if _is_training else 1)
46 | cell_with_attetion = tf.contrib.seq2seq.AttentionWrapper(lstm_cell_drop,
47 | attention_mechanism,
48 | hp.lstm_hidden_size)
49 | return cell_with_attetion
50 |
51 |
52 | def encoder(inputs, hidden_size, encoder_inputs_length, _is_training=True, bi_direction=True, scope="Encoder",
53 | reuse=None):
54 | '''
55 | Args:
56 | inputs: A 2d tensor with shape of [N, T], dtype of int32.
57 | is_training: Whether or not the layer is in training mode.
58 | scope: Optional scope for `variable_scope`
59 | reuse: Boolean, whether to reuse the weights of a previous layer
60 | by the same name.
61 |
62 | Returns:
63 | A collection of Hidden vectors, whose shape is (N, T, E).
64 | '''
65 | with tf.variable_scope(scope, reuse=reuse):
66 | # Encoder
67 | num_units = hidden_size
68 | if bi_direction:
69 | cell_forward = tf.contrib.rnn.MultiRNNCell(
70 | [cell_lstm(num_units, _is_training) for i in range(hp.num_layer_lstm_encode)])
71 | cell_backward = tf.contrib.rnn.MultiRNNCell(
72 | [cell_lstm(num_units, _is_training) for i in range(hp.num_layer_lstm_encode)])
73 | (output_forward, output_backword), (state_forward, state_backward) = tf.nn.bidirectional_dynamic_rnn(
74 | cell_forward,
75 | cell_backward,
76 | inputs,
77 | sequence_length=encoder_inputs_length,
78 | dtype=tf.float32)
79 |
80 | memory = tf.concat([output_forward, output_backword], 2)
81 | state_c = tf.concat([state_forward[2].c, state_backward[2].c], 1)
82 | state_h = tf.concat([state_forward[2].h, state_backward[2].h], 1)
83 | state = tf.contrib.rnn.LSTMStateTuple(state_c, state_h)
84 |
85 |
86 | else:
87 | cell = tf.contrib.rnn.MultiRNNCell(cell_lstm(num_units) * hp.num_encode_lstm)
88 | memory, state = tf.nn.bidirectional_dynamic_rnn(cell,
89 | inputs,
90 | dtype=tf.float32)
91 | return memory, state
92 |
93 |
94 | def decoder(inputs, memory, encode_state, _is_training=True, scope="Decoder", reuse=None):
95 | with tf.variable_scope(scope, reuse=reuse):
96 | # Decoder
97 | vocab_size = len(dict_label2id)
98 | if _is_training:
99 | memory_ = memory
100 | encode_state = encode_state
101 | batch_size = hp.batch_size
102 | else:
103 | if hp.is_beam_search:
104 | memory_ = tf.contrib.seq2seq.tile_batch(memory, multiplier=hp.beam_size)
105 | encode_state = nest.map_structure(lambda s: tf.contrib.seq2seq.tile_batch(s, hp.beam_size),
106 | encode_state)
107 | batch_size = tf.shape(memory)[0] * hp.beam_size
108 | else:
109 | memory_ = memory
110 | encode_state = encode_state
111 | batch_size = tf.shape(memory)[0]
112 |
113 | cell_with_attention = cell_attention_lstm(units=hp.lstm_hidden_size,
114 | input_=memory_,
115 | _is_training=_is_training)
116 | h_decode_initial = cell_with_attention.zero_state(batch_size=batch_size, dtype=tf.float32).clone(
117 | cell_state=encode_state)
118 | output_layer = tf.layers.Dense(vocab_size,
119 | kernel_initializer=tf.truncated_normal_initializer(mean=0.0, stddev=0.1))
120 |
121 | embedding = tf.get_variable('decoder_embedding', [vocab_size, hp.decoder_embedding_size])
122 | embedding = tf.concat((tf.zeros(shape=[1, hp.decoder_embedding_size]), embedding[1:, :]), 0)
123 |
124 | if _is_training:
125 |
126 | decoder_inputs = tf.nn.embedding_lookup(embedding, shift_by_one(inputs))
127 |
128 | targets_length = tf.count_nonzero(inputs, axis=1, dtype=tf.int32)
129 |
130 | max_target_sequence_length = tf.reduce_max(targets_length, name='max_target_len')
131 | mask = tf.sequence_mask(targets_length, max_target_sequence_length, dtype=tf.float32, name='masks')
132 |
133 | training_helper = tf.contrib.seq2seq.TrainingHelper(inputs=decoder_inputs,
134 | sequence_length=targets_length,
135 | time_major=False,
136 | name='training_helper')
137 |
138 | training_decoder = tf.contrib.seq2seq.BasicDecoder(cell=cell_with_attention,
139 | helper=training_helper,
140 | initial_state=h_decode_initial,
141 | output_layer=output_layer)
142 |
143 | outputs, final_state, final_sequence_length = tf.contrib.seq2seq.dynamic_decode(decoder=training_decoder,
144 | impute_finished=True,
145 | maximum_iterations=hp.num_labels) ##解码token的长度
146 |
147 | else:
148 | mask = tf.zeros(shape=[tf.shape(memory)[0], hp.decoder_embedding_size])
149 | start_tokens = tf.fill([tf.shape(memory)[0]], dict_label2id['E'])
150 | end_token = dict_label2id['S']
151 | if hp.is_beam_search:
152 |
153 | inference_decoder = tf.contrib.seq2seq.BeamSearchDecoder(cell=cell_with_attention,
154 | embedding=embedding,
155 | start_tokens=start_tokens,
156 | end_token=end_token,
157 | initial_state=h_decode_initial,
158 | beam_width=hp.beam_size,
159 | output_layer=output_layer)
160 |
161 | else:
162 |
163 | decoding_helper = tf.contrib.seq2seq.GreedyEmbeddingHelper(embedding=embedding,
164 | start_tokens=start_tokens,
165 | end_token=end_token)
166 |
167 | inference_decoder = tf.contrib.seq2seq.BasicDecoder(cell=cell_with_attention,
168 | helper=decoding_helper,
169 | initial_state=h_decode_initial,
170 | output_layer=output_layer)
171 |
172 | outputs, final_state, final_sequence_length = tf.contrib.seq2seq.dynamic_decode(decoder=inference_decoder,
173 | maximum_iterations=hp.max_length)
174 | return outputs, final_state, mask, final_sequence_length
175 |
176 |
177 | def embed(inputs, vocab_size, num_units, zero_pad=True, scope="embedding", reuse=None):
178 | '''Embeds a given tensor.
179 |
180 | Args:
181 | inputs: A `Tensor` with type `int32` or `int64` containing the ids
182 | to be looked up in `lookup table`.
183 | vocab_size: An int. Vocabulary size.
184 | num_units: An int. Number of embedding hidden units.
185 | zero_pad: A boolean. If True, all the values of the fist row (id 0)
186 | should be constant zeros.
187 | scope: Optional scope for `variable_scope`.
188 | reuse: Boolean, whether to reuse the weights of a previous layer
189 | by the same name.
190 |
191 | Returns:
192 | A `Tensor` with one more rank than inputs's. The last dimesionality
193 | should be `num_units`.
194 | '''
195 | with tf.variable_scope(scope, reuse=reuse):
196 | lookup_table = tf.get_variable('lookup_table',
197 | dtype=tf.float32,
198 | shape=[vocab_size, num_units],
199 | initializer=tf.truncated_normal_initializer(mean=0.0, stddev=0.01))
200 | if zero_pad:
201 | lookup_table = tf.concat((tf.zeros(shape=[1, num_units]),
202 | lookup_table[1:, :]), 0)
203 | return tf.nn.embedding_lookup(lookup_table, inputs)
204 |
205 |
206 | if __name__ == '__main__':
207 | print('Done')
208 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/networks.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Wed Jul 8 20:03:12 2020
4 |
5 | @author: cm
6 | """
7 |
8 | import os
9 | import tensorflow as tf
10 |
11 | from classifier_multi_label_seq2seq import modeling
12 | from classifier_multi_label_seq2seq import optimization
13 | from classifier_multi_label_seq2seq.modules import encoder, decoder
14 | from classifier_multi_label_seq2seq.hyperparameters import Hyperparamters as hp
15 | from classifier_multi_label_seq2seq.utils import time_now_string
16 | from classifier_multi_label_seq2seq.classifier_utils import ClassifyProcessor
17 |
18 |
19 | num_labels = hp.num_labels
20 | processor = ClassifyProcessor()
21 | bert_config_file = os.path.join(hp.bert_path, 'albert_config.json')
22 | bert_config = modeling.AlbertConfig.from_json_file(bert_config_file)
23 |
24 |
25 | class NetworkAlbertSeq2Seq(object):
26 | def __init__(self, is_training):
27 | # Training or not
28 | self.is_training = is_training
29 |
30 | # Placeholder
31 | self.input_ids = tf.placeholder(tf.int32, shape=[None, hp.sequence_length], name='input_ids')
32 | self.input_masks = tf.placeholder(tf.int32, shape=[None, hp.sequence_length], name='input_masks')
33 | self.segment_ids = tf.placeholder(tf.int32, shape=[None, hp.sequence_length], name='segment_ids')
34 | self.label_ids = tf.placeholder(tf.int32, shape=[None, None], name='label_ids')
35 |
36 | # Load BERT token features
37 | self.model = modeling.AlbertModel(
38 | config=bert_config,
39 | is_training=self.is_training,
40 | input_ids=self.input_ids,
41 | input_mask=self.input_masks,
42 | token_type_ids=self.segment_ids,
43 | use_one_hot_embeddings=False)
44 |
45 | # Get tensor BERT
46 | self.output_layer_initial = self.model.get_sequence_output()
47 |
48 | # Hidden_size
49 | self.hidden_size = self.output_layer_initial.shape[-1].value
50 |
51 | with tf.name_scope("Encoder"):
52 | # Get input length of encoder
53 | self.input_ids_sequence_length = tf.count_nonzero(self.output_layer_initial, axis=2, dtype=tf.int32)
54 | self.input_ids_length = tf.count_nonzero(self.input_ids_sequence_length, axis=1, dtype=tf.int32)
55 |
56 | # Encoder
57 | self.memory, self.encode_state = encoder(self.output_layer_initial,
58 | self.hidden_size,
59 | self.input_ids_length,
60 | _is_training=self.is_training)
61 | with tf.name_scope("Decoder"):
62 | # Decoder
63 | self.outputs, self.alignments, self.mask, self.final_sequence_length = decoder(self.label_ids,
64 | self.memory,
65 | self.encode_state,
66 | _is_training=is_training)
67 |
68 | # Initial embedding by BERT
69 | ckpt = tf.train.get_checkpoint_state(hp.saved_model_path)
70 | checkpoint_suffix = ".index"
71 | if ckpt and tf.gfile.Exists(ckpt.model_checkpoint_path + checkpoint_suffix):
72 | print('=' * 10, 'Restoring model from checkpoint!', '=' * 10)
73 | print("%s - Restoring model from checkpoint ~%s" % (time_now_string(),
74 | ckpt.model_checkpoint_path))
75 | else:
76 | tvars = tf.trainable_variables()
77 | if hp.init_checkpoint:
78 | (assignment_map, initialized_variable_names) = \
79 | modeling.get_assignment_map_from_checkpoint(tvars,
80 | hp.init_checkpoint)
81 | tf.train.init_from_checkpoint(hp.init_checkpoint, assignment_map)
82 |
83 | # Loss and Optimizer
84 | if self.is_training:
85 | with tf.name_scope("loss"):
86 | # Global step
87 | self.global_step = tf.Variable(0, name='global_step', trainable=False)
88 |
89 | # Prediction
90 | self.predictions = self.outputs.sample_id
91 |
92 | # Loss
93 | per_example_loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=self.label_ids,
94 | logits=self.outputs.rnn_output)
95 |
96 | self.istarget = tf.to_float(tf.not_equal(self.label_ids, 0))
97 | self.loss = tf.reduce_sum(per_example_loss * self.istarget) / (tf.reduce_sum(self.istarget) + 1e-7)
98 |
99 | # Accuracy
100 | self.accuracy = tf.reduce_mean(tf.cast(tf.equal(self.predictions, self.label_ids), tf.float32))
101 |
102 | # Summary for tensorboard
103 | tf.summary.scalar('accuracy', self.accuracy)
104 | tf.summary.scalar('loss', self.loss)
105 | self.merged = tf.summary.merge_all()
106 |
107 | # Optimizer BERT
108 | num_train_examples = len(processor.get_train_examples(hp.data_dir))
109 | num_train_steps = int(
110 | num_train_examples / hp.batch_size * hp.num_train_epochs)
111 | num_warmup_steps = int(num_train_steps * hp.warmup_proportion)
112 | self.optimizer = optimization.create_optimizer(self.loss,
113 | hp.learning_rate,
114 | num_train_steps,
115 | num_warmup_steps,
116 | hp.use_tpu,
117 | Global_step=self.global_step)
118 |
119 | else:
120 | if hp.is_beam_search:
121 | self.predictions = self.outputs.predicted_ids
122 | self.predictions_prob = self.outputs.beam_search_decoder_output[0]
123 | self.predictions_all = self.outputs.beam_search_decoder_output
124 | else:
125 | self.predictions = tf.expand_dims(self.outputs.sample_id, -1)
126 | self.probs = tf.expand_dims(self.outputs.rnn_output, -1)
127 |
128 |
129 | if __name__ == '__main__':
130 | # Load model
131 | albert = NetworkAlbertSeq2Seq(is_training=True)
132 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/optimization.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 |
3 |
4 | import re
5 | import six
6 | from six.moves import zip
7 | import tensorflow.compat.v1 as tf
8 | from tensorflow.contrib import tpu as contrib_tpu
9 | from classifier_multi_label_seq2seq import lamb_optimizer
10 |
11 |
12 | def create_optimizer(loss, init_lr, num_train_steps, num_warmup_steps, use_tpu, Global_step,
13 | optimizer="adamw", poly_power=1.0, start_warmup_step=0):
14 | """Creates an optimizer training op."""
15 |
16 | if Global_step:
17 | global_step = Global_step
18 | else:
19 | global_step = tf.train.get_or_create_global_step()
20 |
21 | learning_rate = tf.constant(value=init_lr, shape=[], dtype=tf.float32)
22 |
23 | # Implements linear decay of the learning rate.
24 | learning_rate = tf.train.polynomial_decay(
25 | learning_rate,
26 | global_step,
27 | num_train_steps,
28 | end_learning_rate=0.0,
29 | power=poly_power,
30 | cycle=False)
31 |
32 | # Implements linear warmup. I.e., if global_step - start_warmup_step <
33 | # num_warmup_steps, the learning rate will be
34 | # `(global_step - start_warmup_step)/num_warmup_steps * init_lr`.
35 | if num_warmup_steps:
36 | tf.logging.info("++++++ warmup starts at step " + str(start_warmup_step)
37 | + ", for " + str(num_warmup_steps) + " steps ++++++")
38 | global_steps_int = tf.cast(global_step, tf.int32)
39 | start_warm_int = tf.constant(start_warmup_step, dtype=tf.int32)
40 | global_steps_int = global_steps_int - start_warm_int
41 | warmup_steps_int = tf.constant(num_warmup_steps, dtype=tf.int32)
42 |
43 | global_steps_float = tf.cast(global_steps_int, tf.float32)
44 | warmup_steps_float = tf.cast(warmup_steps_int, tf.float32)
45 |
46 | warmup_percent_done = global_steps_float / warmup_steps_float
47 | warmup_learning_rate = init_lr * warmup_percent_done
48 |
49 | is_warmup = tf.cast(global_steps_int < warmup_steps_int, tf.float32)
50 | learning_rate = (
51 | (1.0 - is_warmup) * learning_rate + is_warmup * warmup_learning_rate)
52 |
53 | # It is OK that you use this optimizer for finetuning, since this
54 | # is how the model was trained (note that the Adam m/v variables are NOT
55 | # loaded from init_checkpoint.)
56 | # It is OK to use AdamW in the finetuning even the model is trained by LAMB.
57 | # As report in the Bert pulic github, the learning rate for SQuAD 1.1 finetune
58 | # is 3e-5, 4e-5 or 5e-5. For LAMB, the users can use 3e-4, 4e-4,or 5e-4 for a
59 | # batch size of 64 in the finetune.
60 | if optimizer == "adamw":
61 | tf.logging.info("using adamw")
62 | optimizer = AdamWeightDecayOptimizer(
63 | learning_rate=learning_rate,
64 | weight_decay_rate=0.01,
65 | beta_1=0.9,
66 | beta_2=0.999,
67 | epsilon=1e-6,
68 | exclude_from_weight_decay=["LayerNorm", "layer_norm", "bias"])
69 | elif optimizer == "lamb":
70 | tf.logging.info("using lamb")
71 | optimizer = lamb_optimizer.LAMBOptimizer(
72 | learning_rate=learning_rate,
73 | weight_decay_rate=0.01,
74 | beta_1=0.9,
75 | beta_2=0.999,
76 | epsilon=1e-6,
77 | exclude_from_weight_decay=["LayerNorm", "layer_norm", "bias"])
78 | else:
79 | raise ValueError("Not supported optimizer: ", optimizer)
80 |
81 | if use_tpu:
82 | optimizer = contrib_tpu.CrossShardOptimizer(optimizer)
83 |
84 | tvars = tf.trainable_variables()
85 | grads = tf.gradients(loss, tvars)
86 |
87 | # This is how the model was pre-trained.
88 | (grads, _) = tf.clip_by_global_norm(grads, clip_norm=1.0)
89 |
90 | train_op = optimizer.apply_gradients(
91 | list(zip(grads, tvars)), global_step=global_step)
92 |
93 | # Normally the global step update is done inside of `apply_gradients`.
94 | # However, neither `AdamWeightDecayOptimizer` nor `LAMBOptimizer` do this.
95 | # But if you use a different optimizer, you should probably take this line
96 | # out.
97 | new_global_step = global_step + 1
98 | train_op = tf.group(train_op, [global_step.assign(new_global_step)])
99 | return train_op
100 |
101 |
102 | class AdamWeightDecayOptimizer(tf.train.Optimizer):
103 | """A basic Adam optimizer that includes "correct" L2 weight decay."""
104 |
105 | def __init__(self,
106 | learning_rate,
107 | weight_decay_rate=0.0,
108 | beta_1=0.9,
109 | beta_2=0.999,
110 | epsilon=1e-6,
111 | exclude_from_weight_decay=None,
112 | name="AdamWeightDecayOptimizer"):
113 | """Constructs a AdamWeightDecayOptimizer."""
114 | super(AdamWeightDecayOptimizer, self).__init__(False, name)
115 |
116 | self.learning_rate = learning_rate
117 | self.weight_decay_rate = weight_decay_rate
118 | self.beta_1 = beta_1
119 | self.beta_2 = beta_2
120 | self.epsilon = epsilon
121 | self.exclude_from_weight_decay = exclude_from_weight_decay
122 |
123 | def apply_gradients(self, grads_and_vars, global_step=None, name=None):
124 | """See base class."""
125 | assignments = []
126 | for (grad, param) in grads_and_vars:
127 | if grad is None or param is None:
128 | continue
129 |
130 | param_name = self._get_variable_name(param.name)
131 |
132 | m = tf.get_variable(
133 | name=six.ensure_str(param_name) + "/adam_m",
134 | shape=param.shape.as_list(),
135 | dtype=tf.float32,
136 | trainable=False,
137 | initializer=tf.zeros_initializer())
138 | v = tf.get_variable(
139 | name=six.ensure_str(param_name) + "/adam_v",
140 | shape=param.shape.as_list(),
141 | dtype=tf.float32,
142 | trainable=False,
143 | initializer=tf.zeros_initializer())
144 |
145 | # Standard Adam update.
146 | next_m = (
147 | tf.multiply(self.beta_1, m) + tf.multiply(1.0 - self.beta_1, grad))
148 | next_v = (
149 | tf.multiply(self.beta_2, v) + tf.multiply(1.0 - self.beta_2,
150 | tf.square(grad)))
151 |
152 | update = next_m / (tf.sqrt(next_v) + self.epsilon)
153 |
154 | # Just adding the square of the weights to the loss function is *not*
155 | # the correct way of using L2 regularization/weight decay with Adam,
156 | # since that will interact with the m and v parameters in strange ways.
157 | #
158 | # Instead we want ot decay the weights in a manner that doesn't interact
159 | # with the m/v parameters. This is equivalent to adding the square
160 | # of the weights to the loss with plain (non-momentum) SGD.
161 | if self._do_use_weight_decay(param_name):
162 | update += self.weight_decay_rate * param
163 |
164 | update_with_lr = self.learning_rate * update
165 |
166 | next_param = param - update_with_lr
167 |
168 | assignments.extend(
169 | [param.assign(next_param),
170 | m.assign(next_m),
171 | v.assign(next_v)])
172 | return tf.group(*assignments, name=name)
173 |
174 | def _do_use_weight_decay(self, param_name):
175 | """Whether to use L2 weight decay for `param_name`."""
176 | if not self.weight_decay_rate:
177 | return False
178 | if self.exclude_from_weight_decay:
179 | for r in self.exclude_from_weight_decay:
180 | if re.search(r, param_name) is not None:
181 | return False
182 | return True
183 |
184 | def _get_variable_name(self, param_name):
185 | """Get the variable name from the tensor name."""
186 | m = re.match("^(.*):\\d+$", six.ensure_str(param_name))
187 | if m is not None:
188 | param_name = m.group(1)
189 | return param_name
190 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/predict.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu May 30 17:12:37 2019
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import os
10 | # os.environ["CUDA_VISIBLE_DEVICES"] = '-1'
11 | pwd = os.path.dirname(os.path.abspath(__file__))
12 | import sys
13 | sys.path.append(os.path.dirname(os.path.dirname(__file__)))
14 | import tensorflow as tf
15 |
16 | from classifier_multi_label_seq2seq.networks import NetworkAlbertSeq2Seq
17 | from classifier_multi_label_seq2seq.hyperparameters import Hyperparamters as hp
18 | from classifier_multi_label_seq2seq.classifier_utils import get_feature_test
19 |
20 |
21 | class ModelAlbertSeq2seq(object, ):
22 | """
23 | Load Network Albert Seq2seq model
24 | """
25 |
26 | def __init__(self):
27 | self.albert, self.sess = self.load_model()
28 |
29 | @staticmethod
30 | def load_model():
31 | with tf.Graph().as_default():
32 | sess = tf.Session()
33 | with sess.as_default():
34 | albert = NetworkAlbertSeq2Seq(is_training=False)
35 | saver = tf.train.Saver()
36 | sess.run(tf.global_variables_initializer())
37 | checkpoint_dir = os.path.abspath(os.path.join(pwd, hp.file_model_load))
38 | ckpt = tf.train.get_checkpoint_state(checkpoint_dir)
39 | saver.restore(sess, ckpt.model_checkpoint_path)
40 | return albert, sess
41 |
42 |
43 | MODEL = ModelAlbertSeq2seq()
44 | print('Load model finished!')
45 |
46 |
47 | def get_label(sentence):
48 | """
49 | Prediction of the sentence's sentiment.
50 | """
51 |
52 | feature = get_feature_test(sentence)
53 | fd = {MODEL.albert.input_ids: [feature[0]],
54 | MODEL.albert.input_masks: [feature[1]],
55 | MODEL.albert.segment_ids: [feature[2]],
56 | }
57 | output = MODEL.sess.run(MODEL.albert.predictions, feed_dict=fd)
58 | return sorted([hp.dict_id2label[i] for i in output[0][:, 0] if i != 1])
59 |
60 |
61 | def get_labels(sentences):
62 | """
63 | Prediction of some sentences's sentiment.
64 | """
65 | features = [get_feature_test(str(sentence)) for sentence in sentences]
66 | fd = {MODEL.albert.input_ids: [feature[0] for feature in features],
67 | MODEL.albert.input_masks: [feature[1] for feature in features],
68 | MODEL.albert.segment_ids: [feature[2] for feature in features]}
69 | outputs = MODEL.sess.run(MODEL.albert.predictions, feed_dict=fd)
70 | return [sorted([hp.dict_id2label[i] for i in output[:, 0] if i != 1]) for output in outputs]
71 |
72 |
73 | if __name__ == '__main__':
74 | # Test
75 | sentences = ['重量大小出差也很OK',
76 | '轻薄本的通病',
77 | 'logo周围的槽打磨不平整',
78 | '简约外观']
79 | for sentence in sentences:
80 | print(get_label(sentence))
81 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/train.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Fri Jul 10 19:25:55 2020
4 |
5 | @author: cm
6 | """
7 |
8 |
9 |
10 | import os
11 | # os.environ["CUDA_VISIBLE_DEVICES"] = '-1'
12 | import numpy as np
13 | import tensorflow as tf
14 |
15 | from classifier_multi_label_seq2seq.networks import NetworkAlbertSeq2Seq
16 | from classifier_multi_label_seq2seq.classifier_utils import get_features
17 | from classifier_multi_label_seq2seq.hyperparameters import Hyperparamters as hp
18 | from classifier_multi_label_seq2seq.utils import select, shuffle_one, time_now_string
19 | from classifier_multi_label_seq2seq.load import normalization_label
20 |
21 |
22 | pwd = os.path.dirname(os.path.abspath(__file__))
23 | MODEL = NetworkAlbertSeq2Seq(is_training=True)
24 |
25 |
26 | # Get data features
27 | input_ids, input_masks, segment_ids, label_ids = get_features()
28 | num_train_samples = len(input_ids)
29 | arr = np.arange(num_train_samples)
30 | num_batchs = int((num_train_samples - 1) / hp.batch_size) + 1
31 | print('Number of batch:', num_batchs)
32 |
33 | # Set up the graph
34 | saver = tf.train.Saver(max_to_keep=hp.max_to_keep)
35 | sess = tf.Session()
36 | sess.run(tf.global_variables_initializer())
37 |
38 | # Load model saved before
39 | MODEL_SAVE_PATH = os.path.join(pwd, hp.file_model_save)
40 | ckpt = tf.train.get_checkpoint_state(MODEL_SAVE_PATH)
41 | if ckpt and ckpt.model_checkpoint_path:
42 | saver.restore(sess, ckpt.model_checkpoint_path)
43 | print('Restored model!')
44 |
45 | with sess.as_default():
46 | # Tensorboard writer
47 | writer = tf.summary.FileWriter(hp.logdir, sess.graph)
48 | for i in range(hp.num_train_epochs):
49 | indexs = shuffle_one(arr)
50 | for j in range(num_batchs - 1):
51 | i1 = indexs[j * hp.batch_size:min((j + 1) * hp.batch_size, num_train_samples)]
52 |
53 | # Get features
54 | input_id_ = select(input_ids, i1)
55 | input_mask_ = select(input_masks, i1)
56 | segment_id_ = select(segment_ids, i1)
57 | label_id_ = normalization_label(select(label_ids, i1))
58 |
59 | # Feed dict
60 | fd = {MODEL.input_ids: input_id_,
61 | MODEL.input_masks: input_mask_,
62 | MODEL.segment_ids: segment_id_,
63 | MODEL.label_ids: label_id_}
64 |
65 | # Optimizer
66 | sess.run(MODEL.optimizer, feed_dict=fd)
67 |
68 | # Tensorboard
69 | if j % hp.summary_step == 0:
70 | summary, glolal_step = sess.run([MODEL.merged, MODEL.global_step], feed_dict=fd)
71 | writer.add_summary(summary, glolal_step)
72 |
73 | # Save Model
74 | if j % (num_batchs // hp.num_saved_per_epoch) == 0:
75 | if not os.path.exists(os.path.join(pwd, hp.file_model_save)):
76 | os.makedirs(os.path.join(pwd, hp.file_model_save))
77 | saver.save(sess, os.path.join(pwd, hp.file_model_save, 'model' + '_%s_%s.ckpt' % (str(i), str(j))))
78 |
79 | # Log
80 | if j % hp.print_step == 0:
81 | fd = {MODEL.input_ids: input_id_,
82 | MODEL.input_masks: input_mask_,
83 | MODEL.segment_ids: segment_id_,
84 | MODEL.label_ids: label_id_}
85 | loss = sess.run(MODEL.loss, feed_dict=fd)
86 | print('Time:%s, Epoch:%s, Batch number:%s/%s, Loss:%s' % (
87 | time_now_string(), str(i), str(j), str(num_batchs), str(loss)))
88 | print('Train finished')
89 |
--------------------------------------------------------------------------------
/classifier_multi_label_seq2seq/utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Wed Jul 8 19:57:44 2020
4 |
5 | @author: cm
6 | """
7 |
8 | import time
9 | import pandas as pd
10 | import numpy as np
11 |
12 |
13 | def time_now_string():
14 | return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
15 |
16 |
17 | def cut_list(data, size):
18 | """
19 | data: a list
20 | size: the size of cut
21 | """
22 | return [data[i * size:min((i + 1) * size, len(data))] for i in range(int(len(data) - 1) // size + 1)]
23 |
24 |
25 | def select(data, ids):
26 | return [data[i] for i in ids]
27 |
28 |
29 | def shuffle_one(a1):
30 | ran = np.arange(len(a1))
31 | np.random.shuffle(ran)
32 | return a1[ran]
33 |
34 |
35 | def load_txt(file):
36 | with open(file, encoding='utf-8', errors='ignore') as fp:
37 | lines = fp.readlines()
38 | lines = [l.strip() for l in lines]
39 | print("Load data from file (%s) finished !" % file)
40 | return lines
41 |
42 |
43 | def save_txt(file, lines):
44 | lines = [l + '\n' for l in lines]
45 | with open(file, 'w+', encoding='utf-8') as fp:
46 | fp.writelines(lines)
47 | return "Write data to txt finished !"
48 |
49 |
50 | def load_csv(file, header=0, encoding="utf-8"):
51 | return pd.read_csv(file,
52 | encoding=encoding,
53 | header=header,
54 | error_bad_lines=False)
55 |
56 |
57 | def save_csv(dataframe, file, header=True, index=None, encoding="utf-8"):
58 | return dataframe.to_csv(file,
59 | mode='w+',
60 | header=header,
61 | index=index,
62 | encoding=encoding)
63 |
64 |
65 | def save_excel(dataframe, file, header=True, sheetname='Sheet1'):
66 | return dataframe.to_excel(file,
67 | header=header,
68 | sheet_name=sheetname)
69 |
70 |
71 | def load_excel(file, header=0):
72 | return pd.read_excel(file,
73 | header=header,
74 | )
75 |
76 |
77 | def load_vocabulary(file_vocabulary_label):
78 | """
79 | Load vocabulary to dict
80 | """
81 | vocabulary = load_txt(file_vocabulary_label)
82 | dict_id2label, dict_label2id = {}, {}
83 | for i, l in enumerate(vocabulary):
84 | dict_id2label[i] = str(l)
85 | dict_label2id[str(l)] = i
86 | return dict_id2label, dict_label2id
87 |
88 |
89 | def shuffle_two(a1, a2):
90 | ran = np.arange(len(a1))
91 | np.random.shuffle(ran)
92 | a1_ = [a1[l] for l in ran]
93 | a2_ = [a2[l] for l in ran]
94 | return a1_, a2_
95 |
96 |
97 | if __name__ == '__main__':
98 | print('')
99 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/README.md:
--------------------------------------------------------------------------------
1 | # 简介
2 | 1、本项目是在tensorflow版本1.15.0的基础上做的训练和测试。
3 | 2、本项目为中文的多标签文本分类。
4 | 3、欢迎大家联系 www.hellonlp.com
5 | 4、albert_small_zh_google对应的百度云下载地址:
6 | 链接:https://pan.baidu.com/s/1RKzGJTazlZ7y12YRbAWvyA
7 | 提取码:wuxw
8 |
9 |
10 | # 使用方法
11 | 1、准备数据
12 | 数据格式为:classifier_multi_label_textcnn/data/test_onehot.csv
13 | 2、参数设置
14 | 参考脚本 hyperparameters.py,直接修改里面的数值即可。
15 | 3、训练
16 | ```
17 | python train.py
18 | ```
19 | 4、预测
20 | ```
21 | python predict.py
22 | ```
23 | 注意:推理时需要把model/save中的模型复制到model/load中,并修改model/load中的checkpoint文件的内容为当前模型名称,例如:model_checkpoint_path: "model_xx_xx.ckpt"。
24 | 。
25 |
26 |
27 |
28 | # 知乎解读
29 | https://zhuanlan.zhihu.com/p/158622992
30 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/albert_small_zh_google/albert_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "attention_probs_dropout_prob": 0.0,
3 | "hidden_act": "gelu",
4 | "hidden_dropout_prob": 0.0,
5 | "embedding_size": 128,
6 | "hidden_size": 384,
7 | "initializer_range": 0.02,
8 | "intermediate_size": 1536,
9 | "max_position_embeddings": 512,
10 | "num_attention_heads": 12,
11 | "num_hidden_layers": 6,
12 | "num_hidden_groups": 1,
13 | "net_structure_type": 0,
14 | "gap_size": 0,
15 | "num_memory_blocks": 0,
16 | "inner_group_num": 1,
17 | "down_scale_factor": 1,
18 | "type_vocab_size": 2,
19 | "vocab_size": 21128
20 | }
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/data/test.csv:
--------------------------------------------------------------------------------
1 | content,label
2 | 也没有烧伤,|
3 | 主要是之前在北方呆过几几年,|
4 | 温暖过冬,|
5 | 开箱试了一下看着很不错,|
6 | 还可以给宝宝凉衣服,|
7 | 快递师傅送货迅速,|
8 | 快递员差评,|
9 | 沒有什么噪音,|
10 | 就感觉有一部分是强行捏到一起去的,|
11 | 今天刚到货还没有使用,|
12 | 后面还有个设计,|
13 | 非常好用的宝贝,|
14 | 这个开关我是一直没搞懂,|
15 | 该有的功能都有了,|
16 | 我只说一次,|
17 | 收到l等了15天,|
18 | 所以系统默认好评,|
19 | 但是卖家很快就补发了,|
20 | 现在过了二十天了也没退,|
21 | 冬天有她很暖和,制热效果
22 | 收到第一时间体验,|
23 | 刮花了,|
24 | 也不知道耗不好点,|
25 | 表扬一下物流服务,|
26 | 外观设计:不站地方,|
27 | 换了小米,|
28 | 总之我自己用下来,|
29 | 价格实惠量又足,|
30 | 耗电情况:整体来说耗电不是特别严重,产品功耗
31 | 小兔兔外型还不错了,|
32 | 其他特色:利用小爱或者APP可以智能设定,|
33 | 京东的服务超好,|
34 | 这款取暖器已经买两个了,|
35 | 吹到的风才暖,|
36 | 来的及时,|
37 | 全网最便宜,|
38 | 错过了7天试用,|
39 | 连几十块钱的取暖器都不如,|
40 | 家里有小朋友的建议还是使用空调,|
41 | 定时升温控制都很简单,|
42 | 用了近一月,|
43 | 南方湿冷的冬天是很不错的选择,|
44 | 但是我刚开始使用确实有味道,味道
45 | 说是也转到人工客服,|
46 | 只有在旁边能暖一点,|
47 | 2、结构简单,|
48 | 是想象的模样,|
49 | 1档热风和2档热风,|
50 | 除了物流慢一点点,|
51 | 5分钟屋里就暖和了,制热效果
52 | 碰到长期下雨阴雨的天气,|
53 | 颜色比图片还好看,|
54 | 用起来还可以吧,|
55 | 就是感觉大了一点,|
56 | 安全设计:反正加热中我是一点不敢碰的,|
57 | 尤其自营商品,|
58 | 实在是感人,|
59 | 到现在按钮也开不起,|
60 | 开关,|
61 | 放在客房足够用,|
62 | 一会就暖和,制热效果
63 | 回来赶快拆了包装试下效果,|
64 | 性价比还好吧,|
65 | 使用过后一段时间感觉还不错,|
66 | 但是售后服务太差,|
67 | *元多一点,|
68 | 一如既往地又快又好呀,|
69 | 给东北老家买的,|
70 | 再过几分钟就感觉到开始散热了,|
71 | 整个屋子都很暖静音效果:完全没有声音,声音
72 | 比老款要美观,|
73 | 是划线价的一半多一丢丢,|
74 | 这次再买个送婆婆,|
75 | 小太阳完胜,|
76 | 最重要的是,|
77 | 功能强大发热快,制热效果
78 | 塑料味道太大,味道
79 | 格力原装产品,|
80 | 取暖效果:取暖好,制热效果
81 | 是我们买的第二个了,|
82 | 需要个人安装的也不多,|
83 | 没有任何破损的地方,|
84 | 自营售后服务品质有保障,|
85 | 南方冬天暖气是刚需啊,|
86 | 用了两次我和孩子都喉咙发炎了,|
87 | 十几平方的房间开一夜都没觉得有效果,|
88 | 概念&rdquo,|
89 | 外观设计:很不错取暖效果:很好耗电情况:蛮高的静音效果:很不错安全设计:安全其他特色:没有了,产品功耗/制热效果
90 | 我觉得效果比浴霸好,制热效果
91 | 可根据需要选择功率,|
92 | 身边的朋友都有介绍此款,|
93 | 安全设计:设计独特完美,|
94 | 总体来说物美价廉,|
95 | 放书桌旁干活用很合适,|
96 | 如果属于南方的没暖气喜欢的宝宝可以放心购买,|
97 | 还赠送了晾衣架,|
98 | 贴墙放置,|
99 | 非常的美观还耐用,|
100 | 买了你就后悔,|
101 | 说24小时回电话,|
102 | 家里离不开了,|
103 | 估计到冬天没太阳的时候烘衣服方便,|
104 | 十天用了一百六的电费,产品功耗
105 | 有遥控装置,|
106 | 体积虽小,|
107 | 不把手凑上去都感觉不到他的温度,|
108 | 用了两天的,|
109 | 小房间很快就能加热,|
110 | 当天下单第二天就到,|
111 | 加热很快??,|
112 | 静音效果:二档声音不小,|
113 | 也没有长螺丝刀,|
114 | 平均每天就开两小时,|
115 | 但声音还能接受,|
116 | 静音效果:试用了几天,|
117 | 取暖效果:开到二挡很暖和,制热效果
118 | 今年给老人又买一个,|
119 | 看着小,|
120 | 外形取暖效果:超级棒耗电情况:没在意静音效果:安静舒适安全设计:童锁设计省心,产品功耗
121 | 不知道能不能优化,|
122 | 货到手没有一点损坏,|
123 | 今天就到货了,|
124 | 希望要买好好选别家的,|
125 | 利用热空气上升的自然流动,|
126 | 长的差不多也是200左右,|
127 | 现在穿着睡衣,|
128 | 产品的质量不错,|
129 | 希望能耐用,|
130 | 再也不用担心受凉感冒了,|
131 | 一晚耗电30不是梦,产品功耗
132 | 还会有电火花闪现,|
133 | 有一定的防水,|
134 | 双十二特价很便宜,|
135 | 但是一个小时两度电也是真的,|
136 | 在广州用很合适,|
137 | 大人小朋友不容易烫伤,|
138 | 选择小米唯一吸引我的是支持小爱和手机,|
139 | 着实让我觉得有点生气,|
140 | 主要是家里没有安装暖气片,|
141 | 好多人都和我一样的情况,|
142 | 有风扇设计所致,|
143 | 外观设计:很好看取暖效果:取暖很快效果很好,制热效果
144 | 强卖,|
145 | 还危险了,|
146 | 必须要贴着机器才有点温度,|
147 | 耐用也就有安全保障其他特色:冬天衣服难干,|
148 | 时不时的会咔的一声响,|
149 | 就算开低档温度也不错,|
150 | 挺适合南方的冬天,|
151 | 这东西买的真好,|
152 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/data/test_onehot.csv:
--------------------------------------------------------------------------------
1 | content,|,互联互通,产品功耗,滑轮提手,声音,APP操控性,呼吸灯,外观,底座,制热范围,遥控器电池,味道,制热效果,衣物烘干,体积大小
2 | 也没有烧伤,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
3 | 主要是之前在北方呆过几几年,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
4 | 温暖过冬,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
5 | 开箱试了一下看着很不错,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
6 | 还可以给宝宝凉衣服,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7 | 快递师傅送货迅速,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
8 | 快递员差评,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
9 | 沒有什么噪音,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
10 | 就感觉有一部分是强行捏到一起去的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
11 | 今天刚到货还没有使用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
12 | 后面还有个设计,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
13 | 非常好用的宝贝,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
14 | 这个开关我是一直没搞懂,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
15 | 该有的功能都有了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
16 | 我只说一次,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
17 | 收到l等了15天,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
18 | 所以系统默认好评,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
19 | 但是卖家很快就补发了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
20 | 现在过了二十天了也没退,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
21 | 冬天有她很暖和,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
22 | 收到第一时间体验,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
23 | 刮花了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
24 | 也不知道耗不好点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
25 | 表扬一下物流服务,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
26 | 外观设计:不站地方,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
27 | 换了小米,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
28 | 总之我自己用下来,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
29 | 价格实惠量又足,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
30 | 耗电情况:整体来说耗电不是特别严重,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0
31 | 小兔兔外型还不错了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
32 | 其他特色:利用小爱或者APP可以智能设定,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
33 | 京东的服务超好,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
34 | 这款取暖器已经买两个了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
35 | 吹到的风才暖,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
36 | 来的及时,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
37 | 全网最便宜,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
38 | 错过了7天试用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
39 | 连几十块钱的取暖器都不如,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
40 | 家里有小朋友的建议还是使用空调,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
41 | 定时升温控制都很简单,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
42 | 用了近一月,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
43 | 南方湿冷的冬天是很不错的选择,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
44 | 但是我刚开始使用确实有味道,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0
45 | 说是也转到人工客服,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
46 | 只有在旁边能暖一点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
47 | 2、结构简单,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
48 | 是想象的模样,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
49 | 1档热风和2档热风,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
50 | 除了物流慢一点点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
51 | 5分钟屋里就暖和了,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
52 | 碰到长期下雨阴雨的天气,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
53 | 颜色比图片还好看,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
54 | 用起来还可以吧,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
55 | 就是感觉大了一点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
56 | 安全设计:反正加热中我是一点不敢碰的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
57 | 尤其自营商品,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
58 | 实在是感人,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
59 | 到现在按钮也开不起,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
60 | 开关,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
61 | 放在客房足够用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
62 | 一会就暖和,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
63 | 回来赶快拆了包装试下效果,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
64 | 性价比还好吧,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
65 | 使用过后一段时间感觉还不错,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
66 | 但是售后服务太差,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
67 | *元多一点,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
68 | 一如既往地又快又好呀,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
69 | 给东北老家买的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
70 | 再过几分钟就感觉到开始散热了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
71 | 整个屋子都很暖静音效果:完全没有声音,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0
72 | 比老款要美观,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
73 | 是划线价的一半多一丢丢,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
74 | 这次再买个送婆婆,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
75 | 小太阳完胜,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
76 | 最重要的是,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
77 | 功能强大发热快,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
78 | 塑料味道太大,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0
79 | 格力原装产品,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
80 | 取暖效果:取暖好,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
81 | 是我们买的第二个了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
82 | 需要个人安装的也不多,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
83 | 没有任何破损的地方,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
84 | 自营售后服务品质有保障,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
85 | 南方冬天暖气是刚需啊,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
86 | 用了两次我和孩子都喉咙发炎了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
87 | 十几平方的房间开一夜都没觉得有效果,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
88 | 概念&rdquo,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
89 | 外观设计:很不错取暖效果:很好耗电情况:蛮高的静音效果:很不错安全设计:安全其他特色:没有了,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0
90 | 我觉得效果比浴霸好,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
91 | 可根据需要选择功率,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
92 | 身边的朋友都有介绍此款,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
93 | 安全设计:设计独特完美,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
94 | 总体来说物美价廉,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
95 | 放书桌旁干活用很合适,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
96 | 如果属于南方的没暖气喜欢的宝宝可以放心购买,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
97 | 还赠送了晾衣架,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
98 | 贴墙放置,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
99 | 非常的美观还耐用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
100 | 买了你就后悔,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
101 | 说24小时回电话,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
102 | 家里离不开了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
103 | 估计到冬天没太阳的时候烘衣服方便,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
104 | 十天用了一百六的电费,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0
105 | 有遥控装置,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
106 | 体积虽小,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
107 | 不把手凑上去都感觉不到他的温度,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
108 | 用了两天的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
109 | 小房间很快就能加热,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
110 | 当天下单第二天就到,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
111 | 加热很快??,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
112 | 静音效果:二档声音不小,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
113 | 也没有长螺丝刀,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
114 | 平均每天就开两小时,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
115 | 但声音还能接受,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
116 | 静音效果:试用了几天,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
117 | 取暖效果:开到二挡很暖和,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
118 | 今年给老人又买一个,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
119 | 看着小,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
120 | 外形取暖效果:超级棒耗电情况:没在意静音效果:安静舒适安全设计:童锁设计省心,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0
121 | 不知道能不能优化,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
122 | 货到手没有一点损坏,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
123 | 今天就到货了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
124 | 希望要买好好选别家的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
125 | 利用热空气上升的自然流动,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
126 | 长的差不多也是200左右,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
127 | 现在穿着睡衣,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
128 | 产品的质量不错,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
129 | 希望能耐用,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
130 | 再也不用担心受凉感冒了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
131 | 一晚耗电30不是梦,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0
132 | 还会有电火花闪现,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
133 | 有一定的防水,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
134 | 双十二特价很便宜,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
135 | 但是一个小时两度电也是真的,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
136 | 在广州用很合适,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
137 | 大人小朋友不容易烫伤,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
138 | 选择小米唯一吸引我的是支持小爱和手机,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
139 | 着实让我觉得有点生气,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
140 | 主要是家里没有安装暖气片,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
141 | 好多人都和我一样的情况,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
142 | 有风扇设计所致,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
143 | 外观设计:很好看取暖效果:取暖很快效果很好,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
144 | 强卖,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
145 | 还危险了,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
146 | 必须要贴着机器才有点温度,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
147 | 耐用也就有安全保障其他特色:冬天衣服难干,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
148 | 时不时的会咔的一声响,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
149 | 就算开低档温度也不错,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
150 | 挺适合南方的冬天,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
151 | 这东西买的真好,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
152 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/data/vocabulary_label.txt:
--------------------------------------------------------------------------------
1 | |
2 | 互联互通
3 | 产品功耗
4 | 滑轮提手
5 | 声音
6 | APP操控性
7 | 呼吸灯
8 | 外观
9 | 底座
10 | 制热范围
11 | 遥控器电池
12 | 味道
13 | 制热效果
14 | 衣物烘干
15 | 体积大小
16 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/eval.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu Apr 9 10:56:43 2021
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import pandas as pd
10 | from classifier_multi_label_textcnn.utils import load_csv,load_excel,save_csv,shuffle_two,save_txt
11 | from classifier_multi_label_textcnn.predict import get_label_multi
12 | from classifier_multi_label_textcnn.utils import cut_list
13 |
14 |
15 | if __name__ == '__main__':
16 | print("参考下面的方法")
17 | ## 参考 https://github.com/hellonlp/classifier-multi-label/blob/master/evaluation/eval_multi_label.py
18 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/hyperparameters.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Mon Nov 12 14:23:12 2018
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import os
10 | import sys
11 | pwd = os.path.dirname(os.path.abspath(__file__))
12 | sys.path.append(pwd)
13 | from classifier_multi_label_textcnn.utils import load_vocabulary
14 |
15 |
16 | class Hyperparamters:
17 | # Train parameters
18 | num_train_epochs = 50 # 由于训练集较少,所以模型拟合需要更多是epoch。
19 | print_step = 100
20 | batch_size = 64
21 | summary_step = 10
22 | num_saved_per_epoch = 3
23 | max_to_keep = 100
24 | logdir = 'logdir/model_01'
25 |
26 | # Model paths
27 | file_model_save = 'model/model_save'
28 | file_model_load = 'model/model_load'
29 |
30 | # Train/Test data
31 | data_dir = os.path.join(pwd,'data')
32 | train_data = 'train_onehot.csv'
33 | test_data = 'test_onehot.csv'
34 |
35 | # Load vocabulcary dict
36 | dict_id2label,dict_label2id = load_vocabulary(os.path.join(pwd,'data','vocabulary_label.txt') )
37 | label_vocabulary = list(dict_id2label.values())
38 |
39 | # Optimization parameters
40 | warmup_proportion = 0.1
41 | use_tpu = None
42 | do_lower_case = True
43 | learning_rate = 5e-5
44 |
45 | # TextCNN parameters
46 | num_filters = 128
47 | filter_sizes = [2,3,4,5,6,7]
48 | embedding_size = 384
49 | keep_prob = 0.5
50 |
51 | # Sequence and Label
52 | sequence_length = 60
53 | num_labels = len(list(dict_id2label))
54 |
55 | # ALBERT
56 | model = 'albert_small_zh_google'
57 | bert_path = os.path.join(pwd,model)
58 | vocab_file = os.path.join(pwd,model,'vocab_chinese.txt')
59 | init_checkpoint = os.path.join(pwd,model,'albert_model.ckpt')
60 | saved_model_path = os.path.join(pwd,'model')
61 |
62 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/image/graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_textcnn/image/graph.png
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/image/loss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/classifier_multi_label_textcnn/image/loss.png
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/lamb_optimizer.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 |
3 | """Functions and classes related to optimization (weight updates)."""
4 |
5 | from __future__ import absolute_import
6 | from __future__ import division
7 | from __future__ import print_function
8 |
9 | import re
10 | import six
11 | import tensorflow.compat.v1 as tf
12 |
13 | # pylint: disable=g-direct-tensorflow-import
14 | from tensorflow.python.ops import array_ops
15 | from tensorflow.python.ops import linalg_ops
16 | from tensorflow.python.ops import math_ops
17 | # pylint: enable=g-direct-tensorflow-import
18 |
19 |
20 | class LAMBOptimizer(tf.train.Optimizer):
21 | """LAMB (Layer-wise Adaptive Moments optimizer for Batch training)."""
22 | # A new optimizer that includes correct L2 weight decay, adaptive
23 | # element-wise updating, and layer-wise justification. The LAMB optimizer
24 | # was proposed by Yang You, Jing Li, Jonathan Hseu, Xiaodan Song,
25 | # James Demmel, and Cho-Jui Hsieh in a paper titled as Reducing BERT
26 | # Pre-Training Time from 3 Days to 76 Minutes (arxiv.org/abs/1904.00962)
27 |
28 | def __init__(self,
29 | learning_rate,
30 | weight_decay_rate=0.0,
31 | beta_1=0.9,
32 | beta_2=0.999,
33 | epsilon=1e-6,
34 | exclude_from_weight_decay=None,
35 | exclude_from_layer_adaptation=None,
36 | name="LAMBOptimizer"):
37 | """Constructs a LAMBOptimizer."""
38 | super(LAMBOptimizer, self).__init__(False, name)
39 |
40 | self.learning_rate = learning_rate
41 | self.weight_decay_rate = weight_decay_rate
42 | self.beta_1 = beta_1
43 | self.beta_2 = beta_2
44 | self.epsilon = epsilon
45 | self.exclude_from_weight_decay = exclude_from_weight_decay
46 | # exclude_from_layer_adaptation is set to exclude_from_weight_decay if the
47 | # arg is None.
48 | # TODO(jingli): validate if exclude_from_layer_adaptation is necessary.
49 | if exclude_from_layer_adaptation:
50 | self.exclude_from_layer_adaptation = exclude_from_layer_adaptation
51 | else:
52 | self.exclude_from_layer_adaptation = exclude_from_weight_decay
53 |
54 | def apply_gradients(self, grads_and_vars, global_step=None, name=None):
55 | """See base class."""
56 | assignments = []
57 | for (grad, param) in grads_and_vars:
58 | if grad is None or param is None:
59 | continue
60 |
61 | param_name = self._get_variable_name(param.name)
62 |
63 | m = tf.get_variable(
64 | name=six.ensure_str(param_name) + "/adam_m",
65 | shape=param.shape.as_list(),
66 | dtype=tf.float32,
67 | trainable=False,
68 | initializer=tf.zeros_initializer())
69 | v = tf.get_variable(
70 | name=six.ensure_str(param_name) + "/adam_v",
71 | shape=param.shape.as_list(),
72 | dtype=tf.float32,
73 | trainable=False,
74 | initializer=tf.zeros_initializer())
75 |
76 | # Standard Adam update.
77 | next_m = (
78 | tf.multiply(self.beta_1, m) + tf.multiply(1.0 - self.beta_1, grad))
79 | next_v = (
80 | tf.multiply(self.beta_2, v) + tf.multiply(1.0 - self.beta_2,
81 | tf.square(grad)))
82 |
83 | update = next_m / (tf.sqrt(next_v) + self.epsilon)
84 |
85 | # Just adding the square of the weights to the loss function is *not*
86 | # the correct way of using L2 regularization/weight decay with Adam,
87 | # since that will interact with the m and v parameters in strange ways.
88 | #
89 | # Instead we want ot decay the weights in a manner that doesn't interact
90 | # with the m/v parameters. This is equivalent to adding the square
91 | # of the weights to the loss with plain (non-momentum) SGD.
92 | if self._do_use_weight_decay(param_name):
93 | update += self.weight_decay_rate * param
94 |
95 | ratio = 1.0
96 | if self._do_layer_adaptation(param_name):
97 | w_norm = linalg_ops.norm(param, ord=2)
98 | g_norm = linalg_ops.norm(update, ord=2)
99 | ratio = array_ops.where(math_ops.greater(w_norm, 0), array_ops.where(
100 | math_ops.greater(g_norm, 0), (w_norm / g_norm), 1.0), 1.0)
101 |
102 | update_with_lr = ratio * self.learning_rate * update
103 |
104 | next_param = param - update_with_lr
105 |
106 | assignments.extend(
107 | [param.assign(next_param),
108 | m.assign(next_m),
109 | v.assign(next_v)])
110 | return tf.group(*assignments, name=name)
111 |
112 | def _do_use_weight_decay(self, param_name):
113 | """Whether to use L2 weight decay for `param_name`."""
114 | if not self.weight_decay_rate:
115 | return False
116 | if self.exclude_from_weight_decay:
117 | for r in self.exclude_from_weight_decay:
118 | if re.search(r, param_name) is not None:
119 | return False
120 | return True
121 |
122 | def _do_layer_adaptation(self, param_name):
123 | """Whether to do layer-wise learning rate adaptation for `param_name`."""
124 | if self.exclude_from_layer_adaptation:
125 | for r in self.exclude_from_layer_adaptation:
126 | if re.search(r, param_name) is not None:
127 | return False
128 | return True
129 |
130 | def _get_variable_name(self, param_name):
131 | """Get the variable name from the tensor name."""
132 | m = re.match("^(.*):\\d+$", six.ensure_str(param_name))
133 | if m is not None:
134 | param_name = m.group(1)
135 | return param_name
136 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/model/model_load/README.md:
--------------------------------------------------------------------------------
1 | # 训练过程所得模型
2 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/model/model_save/README.md:
--------------------------------------------------------------------------------
1 | # 推理所用的模型
2 |
3 | ## 模型路径
4 | - model_load/checkpoit
5 | - model_load/model_xx_0.ckpt.data-00000-of-00001
6 | - model_load/model_xx_0.ckpt.index
7 | - model_load/model_xx_0.ckpt.meta
8 |
9 | ## checkpoint内容
10 | ```
11 | model_checkpoint_path: "model_xx_0.ckpt"
12 | ```
13 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/modules.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu May 30 21:01:45 2019
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import tensorflow as tf
10 | from tensorflow.contrib.rnn import DropoutWrapper
11 | from classifier_multi_label_textcnn.hyperparameters import Hyperparamters as hp
12 |
13 |
14 | def cell_textcnn(inputs,is_training):
15 | # Add a dimension in final shape
16 | inputs_expand = tf.expand_dims(inputs, -1)
17 | # Create a convolution + maxpool layer for each filter size
18 | pooled_outputs = []
19 | with tf.name_scope("TextCNN"):
20 | for i, filter_size in enumerate(hp.filter_sizes):
21 | with tf.name_scope("conv-maxpool-%s" % filter_size):
22 | # Convolution Layer
23 | filter_shape = [filter_size, hp.embedding_size, 1, hp.num_filters]
24 | W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1),dtype=tf.float32, name="W")
25 | b = tf.Variable(tf.constant(0.1, shape=[hp.num_filters]),dtype=tf.float32, name="b")
26 | conv = tf.nn.conv2d(
27 | inputs_expand,
28 | W,
29 | strides=[1, 1, 1, 1],
30 | padding="VALID",
31 | name="conv")
32 | # Apply nonlinearity
33 | h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
34 | # Maxpooling over the outputs
35 | pooled = tf.nn.max_pool(
36 | h,
37 | ksize=[1, hp.sequence_length - filter_size + 1, 1, 1],
38 | strides=[1, 1, 1, 1],
39 | padding='VALID',
40 | name="pool")
41 | pooled_outputs.append(pooled)
42 | # Combine all the pooled features
43 | num_filters_total = hp.num_filters * len(hp.filter_sizes)
44 | h_pool = tf.concat(pooled_outputs, 3)
45 | h_pool_flat = tf.reshape(h_pool, [-1, num_filters_total])
46 | # Dropout
47 | h_pool_flat_dropout = tf.nn.dropout(h_pool_flat, keep_prob=hp.keep_prob if is_training else 1)
48 | return h_pool_flat_dropout
49 |
50 |
51 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/networks.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu May 30 20:44:42 2019
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import os
10 | import tensorflow as tf
11 | from classifier_multi_label_textcnn import modeling
12 | from classifier_multi_label_textcnn import optimization
13 | from classifier_multi_label_textcnn.modules import cell_textcnn
14 | from classifier_multi_label_textcnn.utils import time_now_string
15 | from classifier_multi_label_textcnn.hyperparameters import Hyperparamters as hp
16 | from classifier_multi_label_textcnn.classifier_utils import ClassifyProcessor
17 |
18 |
19 | num_labels = hp.num_labels
20 | processor = ClassifyProcessor()
21 | bert_config_file = os.path.join(hp.bert_path,'albert_config.json')
22 | bert_config = modeling.AlbertConfig.from_json_file(bert_config_file)
23 |
24 |
25 | class NetworkAlbertTextCNN(object):
26 | def __init__(self,is_training):
27 | # Training or not
28 | self.is_training = is_training
29 | # Placeholder
30 | self.input_ids = tf.placeholder(tf.int32, shape=[None, hp.sequence_length], name='input_ids')
31 | self.input_masks = tf.placeholder(tf.int32, shape=[None, hp.sequence_length], name='input_masks')
32 | self.segment_ids = tf.placeholder(tf.int32, shape=[None, hp.sequence_length], name='segment_ids')
33 | self.label_ids = tf.placeholder(tf.float32, shape=[None,hp.num_labels], name='label_ids')
34 | # Load BERT model
35 | self.model = modeling.AlbertModel(
36 | config=bert_config,
37 | is_training=self.is_training,
38 | input_ids=self.input_ids,
39 | input_mask=self.input_masks,
40 | token_type_ids=self.segment_ids,
41 | use_one_hot_embeddings=False)
42 | # Get the feature vector by BERT
43 | output_layer_init = self.model.get_sequence_output()
44 | # Cell TextCNN
45 | output_layer = cell_textcnn(output_layer_init,self.is_training)
46 | # Hidden size
47 | hidden_size = output_layer.shape[-1].value
48 | # Full-connection
49 | with tf.name_scope("Full-connection"):
50 | output_weights = tf.get_variable(
51 | "output_weights", [num_labels, hidden_size],
52 | initializer=tf.truncated_normal_initializer(stddev=0.02))
53 | output_bias = tf.get_variable(
54 | "output_bias", [num_labels], initializer=tf.zeros_initializer())
55 | logits = tf.nn.bias_add(tf.matmul(output_layer, output_weights, transpose_b=True), output_bias)
56 | # Prediction sigmoid(Multi-label)
57 | self.probabilities = tf.nn.sigmoid(logits)
58 | with tf.variable_scope("Prediction"):
59 | # Prediction
60 | zero = tf.zeros_like(self.probabilities)
61 | one = tf.ones_like(self.probabilities)
62 | self.predictions = tf.where(self.probabilities < 0.5, x=zero, y=one)
63 | with tf.variable_scope("loss"):
64 | # Summary for tensorboard
65 | if self.is_training:
66 | self.accuracy = tf.reduce_mean(tf.to_float(tf.equal(self.predictions, self.label_ids)))
67 | tf.summary.scalar('accuracy', self.accuracy)
68 |
69 | # Initial embedding by BERT
70 | ckpt = tf.train.get_checkpoint_state(hp.saved_model_path)
71 | checkpoint_suffix = ".index"
72 | if ckpt and tf.gfile.Exists(ckpt.model_checkpoint_path + checkpoint_suffix):
73 | print('='*10,'Restoring model from checkpoint!','='*10)
74 | print("%s - Restoring model from checkpoint ~%s" % (time_now_string(),
75 | ckpt.model_checkpoint_path))
76 | else:
77 | print('='*10,'First time load BERT model!','='*10)
78 | tvars = tf.trainable_variables()
79 | if hp.init_checkpoint:
80 | (assignment_map, initialized_variable_names) = \
81 | modeling.get_assignment_map_from_checkpoint(tvars,
82 | hp.init_checkpoint)
83 | tf.train.init_from_checkpoint(hp.init_checkpoint, assignment_map)
84 |
85 | # Loss and Optimizer
86 | if self.is_training:
87 | # Global_step
88 | self.global_step = tf.Variable(0, name='global_step', trainable=False)
89 | per_example_loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=self.label_ids,logits=logits)
90 | self.loss = tf.reduce_mean(per_example_loss)
91 | # Optimizer BERT
92 | train_examples = processor.get_train_examples(hp.data_dir)
93 | num_train_steps = int(
94 | len(train_examples) / hp.batch_size * hp.num_train_epochs)
95 | num_warmup_steps = int(num_train_steps * hp.warmup_proportion)
96 | print('num_train_steps',num_train_steps)
97 | self.optimizer = optimization.create_optimizer(self.loss,
98 | hp.learning_rate,
99 | num_train_steps,
100 | num_warmup_steps,
101 | hp.use_tpu,
102 | Global_step=self.global_step)
103 |
104 | # Summary for tensorboard
105 | tf.summary.scalar('loss', self.loss)
106 | self.merged = tf.summary.merge_all()
107 |
108 |
109 |
110 | if __name__ == '__main__':
111 | # Load model
112 | albert = NetworkAlbertTextCNN(is_training=True)
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/optimization.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Created on Wed Aug 10 19:35:56 2021
4 |
5 | @author: cm
6 | """
7 |
8 | import re
9 | import six
10 | from six.moves import zip
11 | import tensorflow.compat.v1 as tf
12 | from tensorflow.contrib import tpu as contrib_tpu
13 | from classifier_multi_label_textcnn import lamb_optimizer
14 |
15 |
16 | def create_optimizer(loss, init_lr, num_train_steps, num_warmup_steps, use_tpu,Global_step,
17 | optimizer="adamw", poly_power=1.0, start_warmup_step=0):
18 | """Creates an optimizer training op."""
19 |
20 | if Global_step:
21 | global_step = Global_step
22 | else:
23 | global_step = tf.train.get_or_create_global_step()
24 | #global_step = tf.train.get_or_create_global_step()
25 |
26 | learning_rate = tf.constant(value=init_lr, shape=[], dtype=tf.float32)
27 |
28 | # Implements linear decay of the learning rate.
29 | learning_rate = tf.train.polynomial_decay(
30 | learning_rate,
31 | global_step,
32 | num_train_steps,
33 | end_learning_rate=0.0,
34 | power=poly_power,
35 | cycle=False)
36 |
37 | # Implements linear warmup. I.e., if global_step - start_warmup_step <
38 | # num_warmup_steps, the learning rate will be
39 | # `(global_step - start_warmup_step)/num_warmup_steps * init_lr`.
40 | if num_warmup_steps:
41 | tf.logging.info("++++++ warmup starts at step " + str(start_warmup_step)
42 | + ", for " + str(num_warmup_steps) + " steps ++++++")
43 | global_steps_int = tf.cast(global_step, tf.int32)
44 | start_warm_int = tf.constant(start_warmup_step, dtype=tf.int32)
45 | global_steps_int = global_steps_int - start_warm_int
46 | warmup_steps_int = tf.constant(num_warmup_steps, dtype=tf.int32)
47 |
48 | global_steps_float = tf.cast(global_steps_int, tf.float32)
49 | warmup_steps_float = tf.cast(warmup_steps_int, tf.float32)
50 |
51 | warmup_percent_done = global_steps_float / warmup_steps_float
52 | warmup_learning_rate = init_lr * warmup_percent_done
53 |
54 | is_warmup = tf.cast(global_steps_int < warmup_steps_int, tf.float32)
55 | learning_rate = (
56 | (1.0 - is_warmup) * learning_rate + is_warmup * warmup_learning_rate)
57 |
58 | # It is OK that you use this optimizer for finetuning, since this
59 | # is how the model was trained (note that the Adam m/v variables are NOT
60 | # loaded from init_checkpoint.)
61 | # It is OK to use AdamW in the finetuning even the model is trained by LAMB.
62 | # As report in the Bert pulic github, the learning rate for SQuAD 1.1 finetune
63 | # is 3e-5, 4e-5 or 5e-5. For LAMB, the users can use 3e-4, 4e-4,or 5e-4 for a
64 | # batch size of 64 in the finetune.
65 | if optimizer == "adamw":
66 | tf.logging.info("using adamw")
67 | optimizer = AdamWeightDecayOptimizer(
68 | learning_rate=learning_rate,
69 | weight_decay_rate=0.01,
70 | beta_1=0.9,
71 | beta_2=0.999,
72 | epsilon=1e-6,
73 | exclude_from_weight_decay=["LayerNorm", "layer_norm", "bias"])
74 | elif optimizer == "lamb":
75 | tf.logging.info("using lamb")
76 | optimizer = lamb_optimizer.LAMBOptimizer(
77 | learning_rate=learning_rate,
78 | weight_decay_rate=0.01,
79 | beta_1=0.9,
80 | beta_2=0.999,
81 | epsilon=1e-6,
82 | exclude_from_weight_decay=["LayerNorm", "layer_norm", "bias"])
83 | else:
84 | raise ValueError("Not supported optimizer: ", optimizer)
85 |
86 | if use_tpu:
87 | optimizer = contrib_tpu.CrossShardOptimizer(optimizer)
88 |
89 | tvars = tf.trainable_variables()
90 | grads = tf.gradients(loss, tvars)
91 |
92 | # This is how the model was pre-trained.
93 | (grads, _) = tf.clip_by_global_norm(grads, clip_norm=1.0)
94 |
95 | train_op = optimizer.apply_gradients(
96 | list(zip(grads, tvars)), global_step=global_step)
97 |
98 | # Normally the global step update is done inside of `apply_gradients`.
99 | # However, neither `AdamWeightDecayOptimizer` nor `LAMBOptimizer` do this.
100 | # But if you use a different optimizer, you should probably take this line
101 | # out.
102 | new_global_step = global_step + 1
103 | train_op = tf.group(train_op, [global_step.assign(new_global_step)])
104 | return train_op
105 |
106 |
107 | class AdamWeightDecayOptimizer(tf.train.Optimizer):
108 | """A basic Adam optimizer that includes "correct" L2 weight decay."""
109 |
110 | def __init__(self,
111 | learning_rate,
112 | weight_decay_rate=0.0,
113 | beta_1=0.9,
114 | beta_2=0.999,
115 | epsilon=1e-6,
116 | exclude_from_weight_decay=None,
117 | name="AdamWeightDecayOptimizer"):
118 | """Constructs a AdamWeightDecayOptimizer."""
119 | super(AdamWeightDecayOptimizer, self).__init__(False, name)
120 |
121 | self.learning_rate = learning_rate
122 | self.weight_decay_rate = weight_decay_rate
123 | self.beta_1 = beta_1
124 | self.beta_2 = beta_2
125 | self.epsilon = epsilon
126 | self.exclude_from_weight_decay = exclude_from_weight_decay
127 |
128 | def apply_gradients(self, grads_and_vars, global_step=None, name=None):
129 | """See base class."""
130 | assignments = []
131 | for (grad, param) in grads_and_vars:
132 | if grad is None or param is None:
133 | continue
134 |
135 | param_name = self._get_variable_name(param.name)
136 |
137 | m = tf.get_variable(
138 | name=six.ensure_str(param_name) + "/adam_m",
139 | shape=param.shape.as_list(),
140 | dtype=tf.float32,
141 | trainable=False,
142 | initializer=tf.zeros_initializer())
143 | v = tf.get_variable(
144 | name=six.ensure_str(param_name) + "/adam_v",
145 | shape=param.shape.as_list(),
146 | dtype=tf.float32,
147 | trainable=False,
148 | initializer=tf.zeros_initializer())
149 |
150 | # Standard Adam update.
151 | next_m = (
152 | tf.multiply(self.beta_1, m) + tf.multiply(1.0 - self.beta_1, grad))
153 | next_v = (
154 | tf.multiply(self.beta_2, v) + tf.multiply(1.0 - self.beta_2,
155 | tf.square(grad)))
156 |
157 | update = next_m / (tf.sqrt(next_v) + self.epsilon)
158 |
159 | # Just adding the square of the weights to the loss function is *not*
160 | # the correct way of using L2 regularization/weight decay with Adam,
161 | # since that will interact with the m and v parameters in strange ways.
162 | #
163 | # Instead we want ot decay the weights in a manner that doesn't interact
164 | # with the m/v parameters. This is equivalent to adding the square
165 | # of the weights to the loss with plain (non-momentum) SGD.
166 | if self._do_use_weight_decay(param_name):
167 | update += self.weight_decay_rate * param
168 |
169 | update_with_lr = self.learning_rate * update
170 |
171 | next_param = param - update_with_lr
172 |
173 | assignments.extend(
174 | [param.assign(next_param),
175 | m.assign(next_m),
176 | v.assign(next_v)])
177 | return tf.group(*assignments, name=name)
178 |
179 | def _do_use_weight_decay(self, param_name):
180 | """Whether to use L2 weight decay for `param_name`."""
181 | if not self.weight_decay_rate:
182 | return False
183 | if self.exclude_from_weight_decay:
184 | for r in self.exclude_from_weight_decay:
185 | if re.search(r, param_name) is not None:
186 | return False
187 | return True
188 |
189 | def _get_variable_name(self, param_name):
190 | """Get the variable name from the tensor name."""
191 | m = re.match("^(.*):\\d+$", six.ensure_str(param_name))
192 | if m is not None:
193 | param_name = m.group(1)
194 | return param_name
195 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/predict.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu May 30 17:12:37 2021
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import os
10 | import sys
11 | pwd = os.path.dirname(os.path.abspath(__file__))
12 | sys.path.append(os.path.dirname(os.path.dirname(__file__)))
13 | import numpy as np
14 | import tensorflow as tf
15 |
16 | from classifier_multi_label_textcnn.networks import NetworkAlbertTextCNN
17 | from classifier_multi_label_textcnn.classifier_utils import id2label
18 | from classifier_multi_label_textcnn.classifier_utils import get_feature_test
19 | from classifier_multi_label_textcnn.hyperparameters import Hyperparamters as hp
20 |
21 |
22 | class ModelAlbertTextCNN(object,):
23 | """
24 | Load NetworkAlbert TextCNN model
25 | """
26 | def __init__(self):
27 | self.albert, self.sess = self.load_model()
28 | @staticmethod
29 | def load_model():
30 | with tf.Graph().as_default():
31 | sess = tf.Session()
32 | with sess.as_default():
33 | albert = NetworkAlbertTextCNN(is_training=False)
34 | saver = tf.train.Saver()
35 | sess.run(tf.global_variables_initializer())
36 | checkpoint_dir = os.path.abspath(os.path.join(pwd,hp.file_model_load))
37 | print (checkpoint_dir)
38 | ckpt = tf.train.get_checkpoint_state(checkpoint_dir)
39 | saver.restore(sess, ckpt.model_checkpoint_path)
40 | return albert,sess
41 |
42 | MODEL = ModelAlbertTextCNN()
43 | print('Load model finished!')
44 |
45 |
46 | def get_label(sentence):
47 | """
48 | Prediction of the sentence's label.
49 | """
50 | feature = get_feature_test(sentence)
51 | fd = {MODEL.albert.input_ids: [feature[0]],
52 | MODEL.albert.input_masks: [feature[1]],
53 | MODEL.albert.segment_ids:[feature[2]],
54 | }
55 | prediction = MODEL.sess.run(MODEL.albert.predictions, feed_dict=fd)[0]
56 | return [id2label(l) for l in np.where(prediction==1)[0] if l!=0]
57 |
58 |
59 |
60 | if __name__ == '__main__':
61 | # Test
62 | sentences = ['耗电不是特别严重',
63 | '取暖效果:取暖效果好']
64 | for sentence in sentences:
65 | print(sentence,get_label(sentence))
66 |
67 |
68 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/train.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Thu May 30 21:42:07 2021
4 |
5 | @author: cm
6 | """
7 |
8 |
9 |
10 | import os
11 | #os.environ["CUDA_VISIBLE_DEVICES"] = '-1'
12 | import numpy as np
13 | import tensorflow as tf
14 | from classifier_multi_label_textcnn.networks import NetworkAlbertTextCNN
15 | from classifier_multi_label_textcnn.classifier_utils import get_features
16 | from classifier_multi_label_textcnn.hyperparameters import Hyperparamters as hp
17 | from classifier_multi_label_textcnn.utils import select,time_now_string
18 |
19 |
20 | pwd = os.path.dirname(os.path.abspath(__file__))
21 | MODEL = NetworkAlbertTextCNN(is_training=True)
22 |
23 |
24 | # Get data features
25 | input_ids,input_masks,segment_ids,label_ids = get_features()
26 | num_train_samples = len(input_ids)
27 | indexs = np.arange(num_train_samples)
28 | num_batchs = int((num_train_samples - 1) /hp.batch_size) + 1
29 |
30 | # Set up the graph
31 | saver = tf.train.Saver(max_to_keep=hp.max_to_keep)
32 | sess = tf.Session()
33 | sess.run(tf.global_variables_initializer())
34 |
35 | # Load model saved before
36 | MODEL_SAVE_PATH = os.path.join(pwd, hp.file_model_save)
37 | ckpt = tf.train.get_checkpoint_state(MODEL_SAVE_PATH)
38 | if ckpt and ckpt.model_checkpoint_path:
39 | saver.restore(sess, ckpt.model_checkpoint_path)
40 | print('Restored model!')
41 |
42 |
43 | with sess.as_default():
44 | # Tensorboard writer
45 | writer = tf.summary.FileWriter(hp.logdir, sess.graph)
46 | for i in range(hp.num_train_epochs):
47 | np.random.shuffle(indexs)
48 | for j in range(num_batchs-1):
49 | # Get ids selected
50 | i1 = indexs[j * hp.batch_size:min((j + 1) * hp.batch_size, num_train_samples)]
51 |
52 | # Get features
53 | input_id_ = select(input_ids,i1)
54 | input_mask_ = select(input_masks,i1)
55 | segment_id_ = select(segment_ids,i1)
56 | label_id_ = select(label_ids,i1)
57 |
58 | # Feed dict
59 | fd = {MODEL.input_ids: input_id_,
60 | MODEL.input_masks: input_mask_,
61 | MODEL.segment_ids:segment_id_,
62 | MODEL.label_ids:label_id_}
63 |
64 | # Optimizer
65 | sess.run(MODEL.optimizer, feed_dict = fd)
66 |
67 | # Tensorboard
68 | if j%hp.summary_step==0:
69 | summary,glolal_step = sess.run([MODEL.merged,MODEL.global_step], feed_dict = fd)
70 | writer.add_summary(summary, glolal_step)
71 |
72 | # Save Model
73 | if j%(num_batchs//hp.num_saved_per_epoch)==0:
74 | if not os.path.exists(os.path.join(pwd, hp.file_model_save)):
75 | os.makedirs(os.path.join(pwd, hp.file_model_save))
76 | saver.save(sess, os.path.join(pwd, hp.file_model_save, 'model'+'_%s_%s.ckpt'%(str(i),str(j))))
77 |
78 | # Log
79 | if j % hp.print_step == 0:
80 | fd = {MODEL.input_ids: input_id_,
81 | MODEL.input_masks: input_mask_,
82 | MODEL.segment_ids:segment_id_,
83 | MODEL.label_ids:label_id_}
84 | accuracy,loss = sess.run([MODEL.accuracy,MODEL.loss], feed_dict = fd)
85 | print('Time:%s, Epoch:%s, Batch number:%s/%s, Loss:%s, Accuracy:%s'%(time_now_string(),str(i),str(j),str(num_batchs),str(loss),str(accuracy)))
86 | print('Train finished')
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/classifier_multi_label_textcnn/utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Fri May 25 23:43:39 2021
4 |
5 | @author: cm
6 | """
7 |
8 |
9 | import time
10 | import numpy as np
11 | import pandas as pd
12 |
13 |
14 | def cut_list(data,size):
15 | """
16 | data: a list
17 | size: the size of cut
18 | """
19 | return [data[i * size:min((i + 1) * size, len(data))] for i in range(int(len(data)-1)//size + 1)]
20 |
21 |
22 | def time_now_string():
23 | return time.strftime("%Y-%m-%d %H:%M:%S",time.localtime( time.time() ))
24 |
25 |
26 | def select(data,ids):
27 | return [data[i] for i in ids]
28 |
29 |
30 | def load_txt(file):
31 | with open(file,encoding='utf-8',errors='ignore') as fp:
32 | lines = fp.readlines()
33 | lines = [l.strip() for l in lines]
34 | print("Load data from file (%s) finished !"%file)
35 | return lines
36 |
37 |
38 | def save_txt(file,lines):
39 | lines = [l+'\n' for l in lines]
40 | with open(file,'w+',encoding='utf-8') as fp:#a+添加
41 | fp.writelines(lines)
42 | return "Write data to txt finished !"
43 |
44 |
45 | def load_csv(file,header=None):
46 | return pd.read_csv(file,encoding='utf-8',header=header,error_bad_lines=False)#,encoding='gbk'
47 |
48 |
49 | def save_csv(dataframe,file,header=True,index=None,encoding="gbk"):
50 | return dataframe.to_csv(file,
51 | mode='w+',
52 | header=header,
53 | index=index,
54 | encoding=encoding)
55 |
56 |
57 |
58 | def save_excel(dataframe,file,header=True,sheetname='Sheet1'):
59 | return dataframe.to_excel(file,
60 | header=header,
61 | sheet_name=sheetname)
62 |
63 |
64 | def load_excel(file,header=0):
65 | return pd.read_excel(file,
66 | header=header,
67 | )
68 |
69 | def load_vocabulary(file_vocabulary_label):
70 | """
71 | Load vocabulary to dict
72 | """
73 | vocabulary = load_txt(file_vocabulary_label)
74 | dict_id2label,dict_label2id = {},{}
75 | for i,l in enumerate(vocabulary):
76 | dict_id2label[str(i)] = str(l)
77 | dict_label2id[str(l)] = str(i)
78 | return dict_id2label,dict_label2id
79 |
80 |
81 | def shuffle_two(a1,a2):
82 | """
83 | Shuffle two list
84 | """
85 | ran = np.arange(len(a1))
86 | np.random.shuffle(ran)
87 | a1_ = [a1[l] for l in ran]
88 | a2_ = [a2[l] for l in ran]
89 | return a1_, a2_
90 |
91 |
92 | if __name__ == '__main__':
93 | print('')
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/evaluation/README.md:
--------------------------------------------------------------------------------
1 | # 评估函数
2 | - 详情参考:https://zhuanlan.zhihu.com/p/397934047
3 |
--------------------------------------------------------------------------------
/evaluation/eval_multi_label.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Wed Nov 1 15:26:11 2023
4 |
5 | @author: Chen Ming
6 | """
7 |
8 |
9 | class EvalMultiLabel(object):
10 | """ 评估多标签的精确率、召回率和F1值 """
11 |
12 | def __init__(self, y_trues, y_preds):
13 | self.y_trues = y_trues
14 | self.y_preds = y_preds
15 |
16 | def some_samples(y_trues,y_preds):
17 | """ 评估多个样本的TP、FN、FP、TN """
18 | if len(y_trues) == len(y_preds):
19 | tp = 0
20 | fn = 0
21 | fp = 0
22 | tn = 0
23 | for i in range(len(y_trues)):
24 | y_true = y_trues[i]
25 | y_pred = y_preds[i]
26 | tpi,fni,fpi,tni = single_sample(y_true,y_pred)
27 | tp = tp + tpi
28 | fn = fn + fni
29 | fp = fp + fpi
30 | tn = tn + tni
31 | return tp,fn,fp,tn
32 | else:
33 | print('Different length between y_trues and y_preds!')
34 | return 0,0,0,0
35 |
36 | def single_sample(y_true,y_pred):
37 | """ 评估单个样本的TP、FN、FP、TN """
38 | y_true = list(set(y_true))
39 | y_pred = list(set(y_pred))
40 | y_ = list(set(y_true) | set(y_pred))
41 | K = len(y_)
42 | tp1 = 0
43 | fn1 = 0
44 | fp1 = 0
45 | tn1 = 0
46 | for i in range(len(y_)):
47 | if y_[i] in y_true and y_[i] in y_pred:
48 | tp1 = tp1 + 1/K
49 | elif y_[i] in y_true and y_[i] not in y_pred:
50 | fn1 = fn1 + 1/K
51 | elif y_[i] not in y_true and y_[i] in y_pred:
52 | fp1 = fp1 + 1/K
53 | elif y_[i] not in y_true and y_[i] not in y_pred:
54 | tn1 = tn1 + 1/K
55 | return tp1,fn1,fp1,tn1
56 |
57 | self.tp,self.fn,self.fp,self.tn = some_samples(self.y_trues,self.y_preds)
58 | self.recall = self.tp/(self.tp+self.fn)
59 | self.precision = self.tp/(self.tp+self.fp)
60 | self.f1 = 2*self.recall*self.precision/(self.precision+self.recall)
61 |
62 |
63 | if __name__ == '__main__':
64 | y_trues = [['a','b','c'],['a','f','c','h']] #真实标签
65 | y_preds = [['a','d'],['a','d']] #预测标签
66 | EML = EvalMultiLabel(y_trues,y_preds)
67 | print(EML.precision)
68 | print(EML.recall)
69 | print(EML.f1)
70 |
71 |
--------------------------------------------------------------------------------
/imgs/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/01.png
--------------------------------------------------------------------------------
/imgs/01b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/01b.png
--------------------------------------------------------------------------------
/imgs/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/02.png
--------------------------------------------------------------------------------
/imgs/09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/09.png
--------------------------------------------------------------------------------
/imgs/09b.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/09b.jpg
--------------------------------------------------------------------------------
/imgs/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/10.png
--------------------------------------------------------------------------------
/imgs/HELLONLP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/HELLONLP.png
--------------------------------------------------------------------------------
/imgs/base.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/base.png
--------------------------------------------------------------------------------
/imgs/denses.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/denses.jpg
--------------------------------------------------------------------------------
/imgs/denses01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/denses01.png
--------------------------------------------------------------------------------
/imgs/denses02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/denses02.jpg
--------------------------------------------------------------------------------
/imgs/denses02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/denses02.png
--------------------------------------------------------------------------------
/imgs/seq2seq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/seq2seq.png
--------------------------------------------------------------------------------
/imgs/textcnn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hellonlp/classifier-multi-label/7beb1c60a1c3720b175c584f5cbea4bc0c427333/imgs/textcnn.png
--------------------------------------------------------------------------------