1.数据集简介🛴

​ THUCNews是根据新浪新闻RSS订阅频道2005~2011年间的历史数据筛选过滤生成,包含74万篇新闻文档(2.19 GB),均为UTF-8纯文本格式。我们在原始新浪新闻分类体系的基础上,重新整合划分出14个候选分类类别:财经、彩票、房产、股票、家居、教育、科技、社会、时尚、时政、体育、星座、游戏、娱乐。

完整数据集压缩包下载

 

2.数据预处理🚲

​ 在进行特征提取之前,需要对原始文本数据进行预处理,这对于特征提取来说至关重要,一个好的预处理过程会显著的提高特征提取的质量以及分类算法的性能。 中文文本预处理一般包括以下步骤:

​ (1)分词:把文本切分成词或者字。

​ (2)去停用词:文本中大量出现但对分类没有多大作用的词。

​ (3)噪声移除:去除文本中的特殊符号,如特殊标点符号等。这些符号对分类没有太大意义。

​ (4)词频统计语过滤:对文本训练集预处理后,统计剩余单词的词频,并过滤低频词,由剩余的单词构建词典。

 

3.Code🛵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
89
90
91
92
93
94
95
96
97
98
99
100
101
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import json
import os

import torch
import torchtext.vocab as Vocab
import jieba
from tqdm import tqdm
import collections
from config import opt
import numpy as np


def read_cnews():
data = []
# 所有的主题标签
labels = [label for label in os.listdir(opt.data_root) if label != '.DS_Store']
print(labels)
# 标签到整数索引的映射
labels2index = dict(zip(labels, list(range(len(labels)))))
# 整数索引到标签的映射
index2labels = dict(zip(list(range(len(labels))), labels))
print(labels2index)
print(index2labels)

# 存储整数索引到标签的映射,以便预测时使用
with open('index2labels.json', 'w') as f:
json.dump(index2labels, f)
# 存储类别标签
with open('labels.json', 'w') as f:
json.dump(labels, f)

for label in labels:
folder_name = os.path.join(opt.data_root, label)
datasub = [] # 存储某一类的数据[[string,index],.....]
for file in tqdm(os.listdir(folder_name)):
with open(os.path.join(folder_name, file), 'rb') as f:
# 去除文本中空白行
review = f.read().decode('utf-8').replace('\n', '').replace('\r', '').replace('\t', '')
datasub.append([review, labels2index[label]])
data.append(datasub) # 存储所有类的数据[[[string,label],...],[[string,label],...],...]
return data


def split_data(data):
"""
切分数据集为训练集,验证集,测试集
:param data: 数据集
"""
train_data, val_data, test_data = [], [], []

# 对每一类数据进行打乱
# 设置验证集和测试集中每一类样本数据均为200(样本均衡)
for data1 in data: # 遍历每一类数据
np.random.shuffle(data1) # 打乱数据
val_data += data1[:200]
test_data += data1[200:400]
train_data += data1[400:]

np.random.shuffle(train_data) # 打乱数据集
print(len(train_data))
print(len(val_data))
print(len(test_data))

return train_data, val_data, test_data


# 基于训练集构建词典
# 获取停用词
def stopwords(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
stopword = [line.strip() for line in f]
print(stopword[:5])
return stopword


# 分词 去除停用词
def get_tokenized(data, stopword):
"""
:param data:list of [string,label]
"""

def tokenizer(text):
return [tok for tok in jieba.lcut(text) if tok not in stopword]

return [tokenizer(review) for review, _ in data]


# 构建词典
def get_vocab(data, stopword):
tokenized_data = get_tokenized(data, stopword)
counter = collections.Counter([tk for st in tokenized_data for tk in st]) # 统计词频
return Vocab.Vocab(counter, min_freq=5,
specials=['<pad>', '<unk>']) # 保留词频大于5的词 <pad>对应填充项(词典中第0个词) <unk>对应低频词和停止词等未知词(词典中第1个词)


def preprocess_imdb(data, vocab, stopword):
# 将训练集、验证集、测试集中的单词转换为词典中对应的索引
max_len = 500

# 每条新闻通过截断或者补0,使得长度变成500(所有数据统一成一个长度,方便用矩阵并行计算(其实也可以每个 batch填充成一个长度,batch间可以不一样))
def pad(x):
return x[:max_len] if len(x) > max_len else x + [0] * (max_len - len(x))

tokenized_data = get_tokenized(data, stopword)
features = torch.tensor(
[pad([vocab.stoi[word] for word in words]) for words in tokenized_data]) # 把单词转换为词典中对应的索引,并填充成固定长度,封装为tensor
labels = torch.tensor([score for _, score in data])
return features, labels


if __name__ == '__main__':
data = read_cnews()
print(len(data))
train_data, val_data, test_data = split_data(data)
stopword = stopwords('./cn_stopwords.txt')

vocab = get_vocab(train_data, stopword)
print(vocab.itos[:5])
with open('word2index.json', 'w', encoding='utf-8') as f: # 保存词到索引的映射,预测和后续加载预训练词向量时会用到
json.dump(vocab.stoi, f)
print('---------------------')
print(len(vocab))
print(len(vocab.itos))
print(len(vocab.stoi))
print('---------------------')

X_train, y_train = preprocess_imdb(train_data, vocab, stopword)
X_val, y_val = preprocess_imdb(val_data, vocab, stopword)
X_test, y_test = preprocess_imdb(test_data, vocab, stopword)

print('---------------------')
print(len(vocab))
print(len(vocab.itos))
print(len(vocab.stoi))
print("---------------------")

with open('vocabsize.json', 'w') as f: # 保存词典的大小(因为我们基于词频阈值过滤低频词,词典大小不确定,需要保存,后续模型中会用到)
json.dump(len(vocab), f)

# 保存预处理好的训练集、验证集和测试集 以便后续训练时使用
torch.save(X_train, 'X_train.pt')
torch.save(y_train, 'y_train.pt')
torch.save(X_val, 'X_val.pt')
torch.save(y_val, 'y_val.pt')
torch.save(X_test, 'X_test.pt')
torch.save(y_test, 'y_test.pt')

print(X_train.shape, X_train[0])
print(y_train.shape)
print(X_val.shape)
print(y_val.shape)
print(X_test.shape)
print(y_test.shape)