文章目录
一、数据集分析与处理
1、数据分析
数据分析代码位置:
python
analysis.py
首先导入工具包:
python
import pandas as pd
from collections import Counter
import numpy as np
import jieba
主要包含以下内容:
- 第一步: 读数据并统计分类数量
- 第二步: 分析样本分布
- 第三步: 进行分词预处理
- 第四步: 处理验证集和测试集数据
2、读数据并统计分类数量
读取文本文件中的数据,分析了数据的基本信息,包括显示前10行数据、输出总样本数量以及每个类别的样本数量。使用了 pandas 库来处理数据,并使用 Counter 进行样本类别的计数
python
import pandas as pd
from collections import Counter
df = pd.read_csv(
'./data/train.txt',
sep='\t',
header=None,
names=['text', 'label']
)
# 使用 .tolist() 返回 Python 列表(元素自动转为 Python int)
counter = Counter(df['label'].tolist())
print(df.head(10))
print(counter)
print(len(counter))
# text label
# 0 中华女子学院:本科层次仅1专业招男生 3
# 1 两天价网站背后重重迷雾:做个网站究竟要多少钱 4
# 2 东5环海棠公社230-290平2居准现房98折优惠 1
# 3 卡佩罗:告诉你德国脚生猛的原因 不希望英德战踢点球 7
# 4 82岁老太为学生做饭扫地44年获授港大荣誉院士 5
# 5 记者回访地震中可乐男孩:将受邀赴美国参观 5
# 6 冯德伦徐若�隔空传情 默认其是女友 9
# 7 传郭晶晶欲落户香港战伦敦奥运 装修别墅当婚房 1
# 8 《赤壁OL》攻城战诸侯战硝烟又起 8
# 9 "手机钱包"亮相科博会 4
# Counter({3: 18000, 4: 18000, 1: 18000, 7: 18000, 5: 18000, 9: 18000, 8: 18000, 2: 18000, 6: 18000, 0: 18000})
# 10
3、分析样本分布
统计了样本的总量、每个类别样本数量的比例,并计算了每行样本的长度以及文本长度的均值和方差。具体步骤如下:
- 计算样本总量: 通过遍历每个类别的样本数量,累加得到样本总量。
- 计算每个类别样本数量的比例: 遍历每个类别,计算其样本数量在总量中的比例,并输出。
- 统计每行样本的长度: 使用 pandas 库的
apply函数,计算每行样本的长度,并将结果添加到数据框中。 - 统计文本长度的均值和方差: 使用 numpy 库计算文本长度的均值和方差,并输出结果
具体实现如下:
python
import pandas as pd
from collections import Counter
df = pd.read_csv(
'./data/train.txt',
sep='\t',
header=None,
names=['text', 'label']
)
# 使用 .tolist() 返回 Python 列表(元素自动转为 Python int)
counter = Counter(df['label'].tolist())
# 计算样本总量
total = 0
for label_id, count in counter.items():
# print(label_id, count) # 3 18000 ; 4 18000 ....
total += count
print(f'样本总数 = {total}') # 180000
# 每个类别占总数的比例
for label_id, count in counter.items():
print(f'{label_id}: {count / total * 100}%')
# 3: 10.0%
# 4: 10.0%
# 1: 10.0%
# 7: 10.0%
# 5: 10.0%
# 9: 10.0%
# 8: 10.0%
# 2: 10.0%
# 6: 10.0%
# 0: 10.0%
# 每个文本的长度
df['len'] = df['text'].apply(len)
print(df.head(2))
# text label len
# 0 中华女子学院:本科层次仅1专业招男生 3 18
# 1 两天价网站背后重重迷雾:做个网站究竟要多少钱 4 22
# 统计分析文本长度信息, 用于设置每个batch的句子长度
mean = df['len'].mean()
std = df['len'].std()
max_len = df['len'].max()
min_len = df['len'].min()
print(mean, std, max_len, max_len) # 19.21257222222222 3.863797986146837 38 38
# 通常设置的每个 batch 的句子长度为: mean + std 或 mean + 2 * std 或 mean + 3 * std
4、进行分词预处理
注意,模型需要用自身的
my_tokenizer进行分词,这里的分词预处理可以用在随机森林等这种模型,对于fasttext、Bert都有它们自身的分词器
使用了结巴分词库对文本进行分词,并对分词结果进行处理和保存。具体步骤如下:
- 结巴分词: 定义了一个函数
cut_sentence用于进行结巴分词。 - 添加分词结果列: 使用
apply函数将分词结果添加到数据框的新列中,列名为 'words'。 - 分词: 使用
apply函数,将每行文本进行结巴分词,并将结果连接为字符串。 对分词结果进行处理,只保留每行的前30个元素。 - 结果存放到 CSV 文件: 将处理后的结果保存到名为 'train_new.csv' 的 CSV 文件中。
具体实现如下:
python
import pandas as pd
from collections import Counter
import jieba
df = pd.read_csv(
'./data/train.txt',
sep='\t',
header=None,
names=['text', 'label']
)
# 返回的是列表
text_jieba_series = df['text'].apply(jieba.lcut)
print(type(text_jieba_series)) # Series
print(text_jieba_series)
# 0 [中华, 女子, 学院, :, 本科, 层次, 仅, 1, 专业, 招, 男生]
# 1 [两天, 价, 网站, 背后, 重重, 迷雾, :, 做个, 网站, 究竟, 要, 多少, 钱]
# ...
# 179999 [连续, 上涨, 7, 个, 月, , 糖价, 7, 月, 可能, 出现, 调整, 行情]
# 通过空格拼接分词后的列表, 变成字符串
# 这么切片的话, 空格也是会算的, 这种分词就给 随机森林 这种模型使用
df['jieba'] = text_jieba_series.apply(lambda s: ' '.join(s)[ : 30])
print(df)
# text label len jieba
# 0 中华女子学院:本科层次仅1专业招男生 3 18 中华 女子 学院 : 本科 层次 仅 1 专业 招 男生
# 1 两天价网站背后重重迷雾:做个网站究竟要多少钱 4 22 两天 价 网站 背后 重重 迷雾 : 做个 网站 究竟 要
# ... ... ... ... ...
# 179999 连续上涨7个月 糖价7月可能出现调整行情 0 20 连续 上涨 7 个 月 糖价 7 月 可能 出现 调整
df.to_csv('./data/train_jieba.csv')
5、处理测试集和验证集
依次同样的代码处理验证集dev.txt, 测试集test.txt, 同一个文件夹下生成dev_new.csv, test_new.csv文件.
6、整体代码
python
import pandas as pd
from collections import Counter
import jieba
df = pd.read_csv(
# './data/train.txt',
# './data/dev.txt',
'./data/test.txt',
sep='\t',
header=None,
names=['text', 'label']
)
# 使用 .tolist() 返回 Python 列表(元素自动转为 Python int)
counter = Counter(df['label'].tolist())
print(df.head(10))
print(counter)
print(len(counter))
# text label
# 0 中华女子学院:本科层次仅1专业招男生 3
# 1 两天价网站背后重重迷雾:做个网站究竟要多少钱 4
# 2 东5环海棠公社230-290平2居准现房98折优惠 1
# 3 卡佩罗:告诉你德国脚生猛的原因 不希望英德战踢点球 7
# 4 82岁老太为学生做饭扫地44年获授港大荣誉院士 5
# 5 记者回访地震中可乐男孩:将受邀赴美国参观 5
# 6 冯德伦徐若�隔空传情 默认其是女友 9
# 7 传郭晶晶欲落户香港战伦敦奥运 装修别墅当婚房 1
# 8 《赤壁OL》攻城战诸侯战硝烟又起 8
# 9 "手机钱包"亮相科博会 4
# Counter({3: 18000, 4: 18000, 1: 18000, 7: 18000, 5: 18000, 9: 18000, 8: 18000, 2: 18000, 6: 18000, 0: 18000})
# 10
# 计算样本总量
total = 0
for label_id, count in counter.items():
# print(label_id, count) # 3 18000 ; 4 18000 ....
total += count
print(f'样本总数 = {total}') # 180000
# 每个类别占总数的比例
for label_id, count in counter.items():
print(f'{label_id}: {count / total * 100}%')
# 3: 10.0%
# 4: 10.0%
# 1: 10.0%
# 7: 10.0%
# 5: 10.0%
# 9: 10.0%
# 8: 10.0%
# 2: 10.0%
# 6: 10.0%
# 0: 10.0%
# 每个文本的长度
df['len'] = df['text'].apply(len)
print(df.head(2))
# text label len
# 0 中华女子学院:本科层次仅1专业招男生 3 18
# 1 两天价网站背后重重迷雾:做个网站究竟要多少钱 4 22
# 统计分析文本长度信息, 用于设置每个batch的句子长度
mean = df['len'].mean()
std = df['len'].std()
max_len = df['len'].max()
min_len = df['len'].min()
print(mean, std, max_len, max_len) # 19.21257222222222 3.863797986146837 38 38
# 通常设置的每个 batch 的句子长度为: mean + std 或 mean + 2 * std 或 mean + 3 * std
# 返回的是列表
text_jieba_series = df['text'].apply(jieba.lcut)
print(type(text_jieba_series)) # Series
print(text_jieba_series)
# 0 [中华, 女子, 学院, :, 本科, 层次, 仅, 1, 专业, 招, 男生]
# 1 [两天, 价, 网站, 背后, 重重, 迷雾, :, 做个, 网站, 究竟, 要, 多少, 钱]
# ...
# 179999 [连续, 上涨, 7, 个, 月, , 糖价, 7, 月, 可能, 出现, 调整, 行情]
# 通过空格拼接分词后的列表, 变成字符串
# 这么切片的话, 空格也是会算的, 这种分词就给 随机森林 这种模型使用
df['jieba'] = text_jieba_series.apply(lambda s: ' '.join(s)[ : 30])
print(df)
# text label len jieba
# 0 中华女子学院:本科层次仅1专业招男生 3 18 中华 女子 学院 : 本科 层次 仅 1 专业 招 男生
# 1 两天价网站背后重重迷雾:做个网站究竟要多少钱 4 22 两天 价 网站 背后 重重 迷雾 : 做个 网站 究竟 要
# ... ... ... ... ...
# 179999 连续上涨7个月 糖价7月可能出现调整行情 0 20 连续 上涨 7 个 月 糖价 7 月 可能 出现 调整
# df.to_csv('./data/train_jieba.csv')
# df.to_csv('./data/dev_jieba.csv')
df.to_csv('./data/test_jieba.csv')
二、模型训练与测试
1、随机森林模型
编写代码实现随机森林的模型训练. 代码位置:
python
02-random_forest/random_forest.py
导入工具包
python
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
import pandas as pd
from icecream import ic
from sklearn.metrics import accuracy_score
from sklearn.metrics import recall_score
from sklearn.metrics import precision_score
from sklearn.metrics import f1_score
2、读取数据集
python
# 指定数据集的位置
TRAIN_CORPUS = './data/data/train_new.csv'
STOP_WORDS = './data/data/stopwords.txt'
WORDS_COLUMN = 'words'
# 读取数据集
content = pd.read_csv(TRAIN_CORPUS)
# 构建语料库
corpus = content[WORDS_COLUMN].values
3、将文本转换为数值特征
这一部分是 TF-IDF,具体内容跳转到连接:TF-IDF算法详解与TfidfVectorizer全参数精讲:从原理到实战,避开数据泄露与稀疏矩阵陷阱
4、划分数据集,模型训练和模型预测
python
import sys
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
# 1. 读取训练数据
train_df = pd.read_csv(r'./data/train_jieba.csv')
data = train_df['jieba'] # 是分词过后的列, 即 'jieba' 列, 不是原始列 'text'
# print(train_df.head())
# sys.exit()
# 特征工程
transform_train = TfidfVectorizer()
feature_vector_train = transform_train.fit_transform(data) # 训练时 fit + transform
# print(feature_vector_train)
# <Compressed Sparse Row sparse matrix of dtype 'float64'
# with 1183679 stored elements and shape (180000, 109480)>
# Coords Values
# (0, 13697) 0.4222174586819618
# (0, 41112) 0.2791145968866506
# (0, 42588) 0.3651095784926219
# ...
# (1, 13184) 0.3331807679039167
# (1, 85576) 0.49980766176232233
# ...
# (179999, 98582) 0.40797516282178004
# (179999, 83660) 0.5105564066539082
rf_model = RandomForestClassifier()
rf_model.fit(feature_vector_train, train_df['label'])
# 2. 读取测试数据
test_df = pd.read_csv(r'./data/test_jieba.csv')
# print(test_df.head())
# sys.exit()
# 读取的测试数据也要转换为数值特征
data = test_df['jieba'] # 是分词过后的列, 即 'jieba' 列, 不是原始列 'text'
feature_vector_test = transform_train.transform(data) # 预测时只 transform
y_predict = rf_model.predict(feature_vector_test)
print(y_predict) # [3 3 6 ... 2 1 2]
# 模型评估
acc = accuracy_score(test_df['label'], y_predict)
print(acc) # 0.825