基于Python实现的作家风格识别
一 题目背景
1.1 实验背景
作家风格是作家在作品中表现出来的独特的审美风貌。通过分析作品的写作风格来识别作者这一研究有很多应用,比如可以帮助人们鉴定某些存在争议的文学作品的作者、判断文章是否剽窃他人作品等。作者识别其实就是一个文本分类的过程,文本分类就是在给定的分类体系下,根据文本的内容自动地确定文本所关联的类别。 写作风格学就是通过统计的方法来分析作者的写作风格,作者的写作风格是其在语言文字表达活动中的个人言语特征,是人格在语言活动中的某种体现。
1.2 实验要求
- 建立深度神经网络模型,对一段文本信息进行检测识别出该文本对应的作者。
- 绘制深度神经网络模型图、绘制并分析学习曲线。
- 用准确率等指标对模型进行评估。
1.3 实验环境
重点使用到的python包:
import numpy as np
import torch
import jieba as jb
from transformers import BertTokenizer
二 实验内容
2.1 数据集
该数据集包含了 8438 个经典中国文学作品片段,对应文件分别以作家姓名的首字母大写命名。
数据集中的作品片段分别取自 5 位作家的经典作品,分别是:
序号 | 中文名 | 英文名 | 文本片段个数 |
---|---|---|---|
1 | 鲁迅 | LX | 1500 条 |
2 | 莫言 | MY | 2219 条 |
3 | 钱钟书 | QZS | 1419 条 |
4 | 王小波 | WXB | 1300 条 |
5 | 张爱玲 | ZAL | 2000 条 |
2.2 数据预处理
使用 TF-IDF 算法统计各个作品的关键词频率
TF-IDF(term frequency–inverse document frequency,词频-逆向文件频率)是一种用于信息检索与文本挖掘的常用加权技术。
- TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。
- TF-IDF的主要思想是:如果某个单词在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
这里我们使用 jieba 中的默认语料库来进行关键词抽取,取出每位作家频率前500的词
# 词频特征统计,取出各个作家前 500 的词
high_words = set()
for label, text in enumerate(fragment): # 提取每个作家频率前500的词汇,不返回关键词权重值for word in jb.analyse.extract_tags(text, topK=500, withWeight=False):if word in high_words:high_words.remove(word)else:high_words.add(word) # 将高频词汇存入
总共 5 个作者,每个作者的前 500 个最高词频的词作为特征,共 2500 维(或者小于 2500 维)
选用每个作者的前500个高频词汇是因为当选择高频词汇太少的时候,不能充分反映作者的特点,导致各个作者的区分度不高,最终模型准确率较低;但是当高频词汇选择太多的话作家之间的界限会模糊,导致分类失效。通过多次调整,最终确定选择500个高频词。
单独计算每个作者每一句话的这 500 维特征
去除作家中重复出现的高频词汇
jieba分词后,“我们”、“他们”等等的这类汉语常用词汇会大量的出现,这些词汇使用频率高但是机会所有作家都会大量用到这些词汇,这些词汇并不能够区分开各个作家,反而可能影响分类结果,所以选择将这些重复词汇去掉,只选用每个作家的独有词汇用于训练网络,以获取最大的特征。
用这个特征训练神经网络
2.3 搭建模型
详见***算法描述***部分
三 算法描述
3.1 pytorch网络搭建模型
3.1.1 建立number_to_word映射
将五位作者分别对应5个数字,便于后期各种操作。
number_to_word = list(high_words)
word_number = len(number_to_word) # 所有高频词汇的个数
word_to_number = {word: i for i, word in enumerate(number_to_word)} # 建立高频词汇字典,一一对应
3.1.2 划分数据集
在数据预处理的基础上,对读入的数据集进行划分,取出数据中的75%作为训练集,25%作为验证集;之后创建一个***DataLoader*** 对象。
# 划分数据集
valid_weight = 0.25 # 25%验证集
train_size = int((1 - valid_weight) * len(dataset)) # 训练集大小
valid_size = len(dataset) - train_size # 验证集大小
train_dataset, test_dataset = torch.utils.data.random_split(dataset, [train_size, valid_size])
# 创建一个 DataLoader 对象
train_loader = data.DataLoader(train_dataset, batch_size=16, shuffle=True) # batch_size=16
valid_loader = data.DataLoader(test_dataset, batch_size=1000, shuffle=True) # batch_size=1000
3.1.3 确定训练模型、优化器、损失函数、学习率(最终确定版本及具体确定过程见4.1)
首先,我们使用Sequential 序贯模型,通过将层的列表传递给Sequential的构造函数,来创建一个Sequential模型。之后,在Sequential 序贯模型中使用`nn.Linear()`设置隐藏层;同时使用`nn.ReLU()`线性整流函数使输入小于0的值输出幅值为0,输入大于零的值幅值不变(激活函数)。其次,使用`nn.CrossEntropyLoss()`计算交叉熵损失。最后,构造优化器,使用使用`torch.optim`这一个实现了多种优化算法的包,之后调用`adam`算法,Adam(Adaptive Moment Estimation)本质上是带有动量项的RMSprop,它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。它的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。
神经网络结构如图所示:
图1 神经网络模型图
神经网络包含三个隐藏层,输出层个数为六,包含五个作者和一个无法识别选项。三个隐含层神经元个数通过多次实验测试最终选定为1024、1024和1024,在此输入输出条件下正确率比其它条件略高。初始学习率设定为1e-4,这是通过多次调整测试得到的当前情况下的一个最优值。
部分代码如下:
# 设定模型参数,使用ReLU作为激活函数,简单顺序连接模型
model = nn.Sequential(nn.Linear(word_number, 512), # 三个隐含层神经网络,尝试(512,1024,1024)nn.ReLU(), # 激活函数尝试ReLU,nn.Linear(512, 1024),nn.ReLU(),nn.Linear(1024, 1024),nn.ReLU(),nn.Linear(1024, 6), # 最后一个隐含层不需要激活函数
).to(device)epochs = 20 # 设定训练轮次
loss_fn = nn.CrossEntropyLoss() # 定义损失函数(尝试nn.CrossEntropyLoss()和nn.NLLLoss(),二者多用于多分类任务)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4) # 定义优化器(adam),初始学习率为1e-4
best_acc = 0 # 优化器尝试RMSProp()、Adam()、Adamax()
history_acc = []
history_loss = []
best_model = model.cpu().state_dict().copy() # 最优模型
3.1.4 训练
共训练20轮,训练结束绘制`valid_loss`曲线和`valid_acc`曲线,并输出最佳准确率
图2 valid_loss/valid_acc曲线
图3 valid_acc曲线
四 求解结果
4.1 pytorch模型
4.1.1 jieba分词选择:精确模式与搜索引擎模式
在其它条件相同的情况下(`epochs=20`,`Adam`,`nn.CrossEntropyLoss()`,`三个隐含层512,1024,1024`)尝试`jb.lcut_for_search()`搜索引擎模式和`jb.lcut()`精确分词模式
jb.lcut_for_search()
训练20轮,正确率44/50
jb.lcut()
训练20轮,正确率45/50
故后续使用精准模式探索最优参数条件。
4.1.2 损失函数选择
尝试`nn.CrossEntropyLoss()`和`nn.NLLLoss()`,二者多用于多分类任务
(Adam
,三个隐含层512,1024,1024
,jb.lcut()
)
nn.CrossEntropyLoss()
训练20轮,正确率45/50
nn.NLLLoss()
训练20轮,正确率10/50
故最终选择`nn.CrossEntropyLoss()`作为损失函数
4.1.3 优化器选择
优化器尝试`RMSProp()`、`Adam()`、`Adamax()`
(nn.CrossEntropyLoss()
,三个隐含层512,1024,1024
,jb.lcut()
)
Adamax()
训练40轮,测试正确率44/50
Adam()
训练40轮,测试正确率45/50
故最终选择`Adam()`作为优化器
4.1.4 分词个数以及batch size调整
由于删去了大量的重复分词,所以可以将分词个数加大以获取更多的特征,调整分词个数到500。同时发现,batch size调整到较小的数量时,最终的效果会更好,故调整batch size 到16,进行测试,结果如下:
http://www.biyezuopin.vip
分词500,batch size 32
图6 valid_loss/valid_acc曲线
分词500,batch size 16
故最终选择分词个数为500个,batch size 16
4.1.5 model参数调整
通过多次测试对比,发现使用一个隐含层,隐含层单元个数为700的时候,选用ReLU作为激活函数,可以取到最佳的效果。最终确定最优参数条件下训练40轮输出结果如下:
最终确定最优参数条件下训练60轮输出结果如下:
图5 valid_loss/valid_acc曲线
epoch:0 | valid_acc:0.2726
epoch:1 | valid_acc:0.4793
epoch:2 | valid_acc:0.6053
………………………………………………
epoch:58 | valid_acc:0.9435
epoch:59 | valid_acc:0.9592
best accuracy:1.0000
最终最佳准确率为100%,`accuracy`曲线逐渐趋近0.98,`loss`曲线逐渐趋近0,随着训练次数的增加,`loss`不断减小。故最终确定参数为:
- 损失函数:
nn.CrossEntropyLoss()
- 神经网络结构:
一个隐含层700
- 分词模式:
jb.lcut()
- 优化器:
Adam()
- 验证集占比:25%
- 训练轮次:60
- 分词个数:每个作家500,去除重复分词
- batch size:16
提交测试:
在系统中提交模型,在50个测试文本中,识别正确50个,准确率达到100%,效果与最终最佳准确率相近,效果良好。
五 比较分析
best-Accuracy | valid-loss | 结果 | |
---|---|---|---|
初始模型 | 94.55% | 0.065 | 44/50 |
优化模型 | 100% | 0.039 | 50/50 |
比较可知,基于各路优化模型效果稍微优于最初模型,最终测试集准确率可达***100%***。
六 心得与感想
本次实验过程中主要使用了基于***pytorch***的方法进行训练,由于初次使用这些方法,所以前期实现的过程相对困难。最初我想通过调用GPU资源来进行训练但是一直没有调用成功,所以退而求其次使用CPU进行训练,这就使得每次的训练时间都很长,调整参数的过程花费了很多的时间。虽然过程非常的辛苦,但最终的结果还是很不错的,最终整体全部正确,准确率还是很高的。总的来说在过程中收获还是很大的,受益匪浅。
源代码如下:
import os
import numpy as np
import jieba as jb
import jieba.analyse
import torch
import torch.nn as nn
from torch.utils import data
import matplotlib.pyplot as pltdevice = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')number_to_author = ['LX', 'MY', 'QZS', 'WXB', 'ZAL'] # 作家集合
author_number = len(number_to_author) # 作家数量
author_to_number = {author: i for i, author in enumerate(number_to_author)} # 建立作家数字映射,从0开始# 读入数据集
data_begin = [] # 初始数据集合
path = 'dataset/' # 数据路径
# path = 'test_data/test_case1_data/' # 数据路径
for file in os.listdir(path):if not os.path.isdir(file) and not file[0] == '.': # 跳过隐藏文件和文件夹with open(os.path.join(path, file), 'r', encoding='UTF-8') as f: # 打开文件for line in f.readlines():data_begin.append((line, author_to_number[file[:-4]]))# 将片段组合在一起后进行词频统计
fragment = ['' for _ in range(author_number)]
for sentence, label in data_begin:fragment[label] += sentence # 每个作家的所有作品组合到一起# 词频特征统计,取出各个作家前 500 的词
high_words = set()
for label, text in enumerate(fragment): # 提取每个作家频率前500的词汇,不返回关键词权重值for word in jb.analyse.extract_tags(text, topK=500, withWeight=False):if word in high_words:high_words.remove(word)else:high_words.add(word) # 将高频词汇存入number_to_word = list(high_words)
word_number = len(number_to_word) # 所有高频词汇的个数
word_to_number = {word: i for i, word in enumerate(number_to_word)} # 建立高频词汇字典,一一对应features = torch.zeros((len(data_begin), word_number))
labels = torch.zeros(len(data_begin))
for i, (sentence, author_belong) in enumerate(data_begin):feature = torch.zeros(word_number, dtype=torch.float)for word in jb.lcut(sentence): # jb.lcut 直接生成的就是一个list,尝试jb.lcut_for_search()搜索引擎模式和jb.lcut精确分词模式if word in high_words:feature[word_to_number[word]] += 1if feature.sum():feature /= feature.sum()features[i] = featurelabels[i] = author_belongelse:labels[i] = 5 # 表示识别不了作者dataset = data.TensorDataset(features, labels)# 划分数据集
valid_weight = 0.25 # 25%验证集
train_size = int((1 - valid_weight) * len(dataset)) # 训练集大小
valid_size = len(dataset) - train_size # 验证集大小
train_dataset, test_dataset = torch.utils.data.random_split(dataset, [train_size, valid_size])
# 创建一个 DataLoader 对象
train_loader = data.DataLoader(train_dataset, batch_size=16, shuffle=True) # batch_size=16
valid_loader = data.DataLoader(test_dataset, batch_size=1000, shuffle=True) # batch_size=1000# 设定模型参数,使用ReLU作为激活函数,简单顺序连接模型
model = nn.Sequential(nn.Linear(word_number, 700), # 一个隐含层神经网络,尝试(700)nn.ReLU(), # 激活函数尝试ReLU,nn.Linear(700, 6),# 最后一个隐含层不需要激活函数
).to(device)epochs = 60 # 设定训练轮次
loss_fn = nn.CrossEntropyLoss() # 定义损失函数(尝试nn.CrossEntropyLoss()和nn.NLLLoss(),二者多用于多分类任务)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4) # 定义优化器(adam),初始学习率为1e-4
best_acc = 0 # 优化器尝试RMSProp()、Adam()、Adamax()
history_acc = []
history_loss = []
best_model = model.cpu().state_dict().copy() # 最优模型for epoch in range(epochs): # 开始训练for step, (word_x, word_y) in enumerate(train_loader):word_x = word_x.to(device) # 传递数据word_y = word_y.to(device)out = model(word_x)loss = loss_fn(out, word_y.long()) # 计算损失optimizer.zero_grad()loss.backward() # 反向传播optimizer.step()train_acc = np.mean((torch.argmax(out, 1) == word_y).cpu().numpy())with torch.no_grad(): # 上下文管理器,被包裹语句不会被trackfor word_x, word_y in valid_loader:word_x = word_x.to(device)word_y = word_y.to(device)out = model(word_x) valid_acc = np.mean((torch.argmax(out, 1) == word_y).cpu().numpy()) # 准确率求平均if valid_acc > best_acc: # 记录最佳模型best_acc = valid_accbest_model = model.cpu().state_dict().copy()print('epoch:%d | valid_acc:%.4f' % (epoch, valid_acc)) # 展示训练过程history_acc.append(valid_acc)history_loss.append(loss)print('best accuracy:%.4f' % (best_acc, ))
torch.save({'word2int': word_to_number,'int2author': number_to_author,'model': best_model,
}, 'results/test1.pth') # 保存模型plt.plot(history_acc,label = 'valid_acc')
plt.plot(history_loss,label = 'valid_loss')
plt.legend()
plt.show()
基于Python实现的作家风格识别相关推荐
- 基于python+OpenCV的车牌号码识别
基于python+OpenCV的车牌号码识别 车牌识别行业已具备一定的市场规模,在电子警察.公路卡口.停车场.商业管理.汽修服务等领域已取得了部分应用.一个典型的车辆牌照识别系统一般包括以下4个部分: ...
- 基于Python的百度AI人脸识别API接口(可用于OpenCV-Python人脸识别)
基于Python的百度AI人脸识别API接口(可用于OpenCV-Python人脸识别) 资源: download.csdn.net/download/weixin_53403301/43644312 ...
- python人脸深度识别_基于Python的深度学习人脸识别方法
基于 Python 的深度学习人脸识别方法 薛同来 ; 赵冬晖 ; 张华方 ; 郭玉 ; 刘旭春 [期刊名称] <工业控制计算机> [年 ( 卷 ), 期] 2019(032)002 [摘 ...
- 基于Python的去雾人脸识别算法
本文记录的是本人在学校小学期实现的基于Python的去雾+人脸识别算法,本次项目的很多资源都来源于网上的文献与博客,下面是项目的内容. 这次项目的完成主要分为三部分:去雾算法,人脸识别,去雾算法与人脸 ...
- python人脸识别opencv_基于python+OpenCV模块的人脸识别定位技术
什么是OpenCV模块 OpenCV是一款跨平台的视觉库,可以支持的操作系统有Linux.Windows和Mac OS操作系统,并且还提供了多种语言的接口,比如Python,java,MATLAB等常 ...
- 基于python的transform行人车辆识别
Transformer是一种神经网络体系结构,由于它能够有效地处理顺序数据中的长期依赖性,因此在自然语言处理(NLP)任务中受到欢迎.它还被应用于各种其他任务,包括图像分类.对象检测和语音识别. 在车 ...
- 基于Python的KNN数字验证码识别
一.主要内容 本项目基于Python爬虫爬取验证码图片,对图片进行去噪.分割,通过KNN算法训练模型,实现验证其准确率. 二.系统流程 首先从指定的网页中爬取验证码图片数据,然后对数据进行一个去噪和分 ...
- 基于Python的车牌检测和识别系统
向AI转型的程序员都关注了这个号???????????? 人工智能大数据与深度学习 公众号:datayx 1.车牌检测和识别项目介绍 车牌的检测和识别的应用非常广泛,比如交通违章车牌追踪,小区或地下 ...
- 基于python和opencv的人脸识别
该图像识别需要安装opencv,opencv的安装方法有两种,可通过网上提示的进行安装,本人是由pycharm里面直接安装的. 也可以直接根据python的版本通过cmd命令进行安装其命令为pip i ...
最新文章
- Codechef July Challenge 2018 : Subway Ride
- java调用so库中的native方法_Java如何调用本地.so库里的方法
- 减少生活中的不确定性
- 微信小程序小模块界面,边框带阴影
- 【MFC】带图标的工具栏
- java ee的小程序_Java EE 8 –为更多设备提供更多应用程序
- python pool_派松水潭(Python Pool)
- Calendar类 set方法 get方法 add方法
- 创建用户的种类与区分
- LVS Nginx 负载均衡区别
- spark基础之Spark SQL和Hive的集成以及ThriftServer配置
- R开发环境(Eclipse+StatET)
- (小白)系统盘制作(WPE工具箱)及镜像下载
- Altium Designer
- 我的知识管理工具列表 zz
- 鸵鸟腿“半兽人”已学会跟踪避障,可自主移动到安全距离
- 配置zsh的prompt
- 使用word2vec分析红楼梦中的人物
- IOS 应用内打开三方地图app直接导航(高德坐标)
- preventExtention、freeze、seal及isExtensible、isFrozen 、isSealed比较
热门文章
- GBA火焰纹章改版-智慧的结晶
- 期货ios模拟交易软件
- #今日论文推荐# 中国矿大团队,开发集成多尺度深度学习模型,用于 RNA 甲基化位点预测
- spring配置与监听mysql_spring boot (8)mybatis配置监听,
- RK3568平台开发系列讲解(蓝牙篇)蓝牙通讯协议PhoneBookAccessProfile(PBAP)同步通讯录
- Mac上绘制流程图的软件
- Keras : 利用卷积神经网络CNN对图像进行分类,以mnist数据集为例建立模型并预测
- 关于极光APP推送JPUSH的服务器端使用
- Linux下常用软件安装指南
- 【思维导图怎么画】万彩脑图大师教程 | 添加主题与线条