NLP入门——基于梯度下降法分类的应用

问题分析

我们前面研究的都是基于统计的方法,通过不同的统计方法得到不同的准确率,通过改善统计的方式来提高准确率。现在我们要研究基于数学的方式来预测准确率。

假设我们有一个分词 s_{class,word},class是该对象的类别,word是该分词的词频。
则该分词在某类别中的分数为:s_(class) = sum(s{class,word})
那么我们欲求一个正确的类别:s_(class_g),如何使正确的类别的分数最高呢?
这里引入了损失函数 l o s s = s ( c l a s s m ) − s ( c l a s s g ) loss = s(class_m) - s(class_g) loss=s(classm)s(classg) 即用最大类别的分数 - 正确类别的分数。如果正确类别即是分数最大的类别,则loss为0;否则loss将为一个大于0的数。
如果loss函数越来越小,则说明正确的类别越来越多,准确率也越来越高。因此我们需要找到一个 s_(class),来使得loss最小。

一元函数的梯度下降法示例

图片来自吴恩达深度学习教程。
在这里插入图片描述
假定代价函数loss为J(w),则有 J ( w ) = J ( w ) − α ∗ d J ( w ) d w J(w)=J(w)-α*\frac {dJ(w)}{dw} J(w)=J(w)αdwdJ(w)
α为学习率,用来控制每步的步长,即向下走一步的长度dJ(w)/dw就是函数J(w)对w求导
在这里插入图片描述
如图所示,该点的导数就是这个点相切于J(w)的小三角的高除以宽,如果我们从图中点为初始点开始梯度下降算法,则该点斜率符号为正,即dJ(w)/dw > 0,因此接下来会向左走一步。
在这里插入图片描述
整个梯度下降法的迭代过程就是不断向左走,直到逼近最小值点
在这里插入图片描述
若我们以如图点为初始化点,则该点处斜率为负,即dJ(w)/dw < 0,所以接下来会向右走:
在这里插入图片描述
整个梯度下降法的迭代过程就是不断向右走,即朝着最小值点的方向走。

梯度下降过程中有两个问题:
1.步长 不能过大,可能会跳过最小值点
答:设置学习率不断减小
2.局部最优解未必是全局最优解
答:参数量越大,找到全局最小值的概率越小,局部最小值就越接近全局最小值

基于梯度下降法的分类

所以我们可以有如下分析:
若class_max == class_g:
loss = 0
若class_max > class_g:
l o s s = s u m ( s c l a s s m , w o r d ) − s u m ( s c l a s s g , w o r d ) loss = sum(s_{class_m,word}) - sum(s_{class_g,word}) loss=sum(sclassm,word)sum(sclassg,word)
则有
d ( l o s s ) d ( s c l a s s m , w o r d ) = 1 \frac {d( loss) }{d (s_{class_m,word})} = 1 d(sclassm,word)d(loss)=1
d ( l o s s ) d ( s c l a s s g , w o r d ) = − 1 \frac{d( loss) }{d (s_{class_g,word})} = -1 d(sclassg,word)d(loss)=1
我们将loss沿着导数方向移动:
s c l a s s m , w o r d − = l r ∗ 1 s_{class_m,word} -= lr*1 sclassm,word=lr1
s c l a s s g , w o r d − = l r ∗ ( − 1 ) s_{class_g,word} -= lr*(-1) sclassg,word=lr(1)
化简则有:
s c l a s s m , w o r d − = l r s_{class_m,word} -= lr sclassm,word=lr
s c l a s s g , w o r d + = l r s_{class_g,word} += lr sclassg,word+=lr
这会使得错误的s_{class_max,word}分数减小,使得正确的s_{class_g,word}分数增大,使得s(class_g)趋近于s(class_max),使loss函数值不断减小。
最初准确率会快速上升,这是调参的结果。当准确率逐渐稳定出现震荡时,我们得到的即为全局最优解,我们绕开统计的方法,找到一组权重求得最优解。

#learnw.py
#encoding: utf-8

import sys
from json import dump
from math import log, sqrt, inf
from random import uniform, seed
from tqdm import tqdm

def build_model(srcf, tgtf):

    #{class: {word: freq}}
    _c, _w = set(), set() #_c 为所有的类别,_w 为所有的词
    with open(srcf,"rb") as fsrc, open(tgtf,"rb") as ftgt:
        for sline, tline in zip(fsrc, ftgt):
            _s, _t = sline.strip(), tline.strip() # s,t分别为句子和类别
            if _s and _t:
                _s, _class = _s.decode("utf-8"), _t.decode("utf-8")
                if _class not in _c:#如果_c中没有这个类别
                    _c.add(_class)#添加到_c中
                for word in _s.split():#遍历一行里面空格隔开的每个分词
                    if word not in _w:#如果_w中没有这个类别
                        _w.add(word)#添加到_w中
    
    _ =sqrt( 2.0 / (len(_c) + len(_w))) #设置随机数,一定要小
   
    return {_class: {_word: uniform(-_, _) for _word in _w} for _class in _c}              
    #为每个类别创建一个词典,词典中是子词和随机到的初始点w

def compute_instance(model, lin):
    
    rs = {}
    _max_score, _max_class = -inf, None
    for _class, v in model.items(): #对模型中的每一类和类型的词典 v:{word: freq}
        rs[_class] = _s = sum(v.get(_word, 0.0) for _word in lin) #这个类的分数即为该类中所有子词分数之和
        if _s > _max_score:
            _max_score = _s
            _max_class = _class
        #获取分数最高的类别
        
    return rs, _max_class, _max_score#返回每个类别的分数、最大分数的类别、最大的分数
#返回每一类的频率分布,在这行中所有分词分数之和最大的类,最大的分数 
 

def train(srcf, tgtf, model, base_lr=0.1, max_run=128):#学习率初始为0.1,训练128轮
    
    with open(srcf,"rb") as fsrc, open(tgtf,"rb") as ftgt:
        for _epoch in tqdm(range(1,max_run+1)): #在每轮中
            fsrc.seek(0)    #对文件做复位,每次读取文件开头
            ftgt.seek(0)
            _lr = base_lr / sqrt(_epoch) #将学习率不断变小,控制步长越来越小
            total = err = 0
            for sline, tline in zip(fsrc, ftgt):#对每行数据
                _s, _t = sline.strip(), tline.strip()
                if _s and _t:
                    _s, _class = _s.decode("utf-8").split(), _t.decode("utf-8")#_s为子词,_t为类别
                    scores, max_class, max_score = compute_instance(model, _s)#算出每行中每个类别的分数、最大分数的类别、最大的分数
                    if max_class != _class:#判断最大的类别和标准答案的类别是否相等
                        for _word in _s:
                            model[max_class][_word] -= _lr  #错误的类别
                            model[_class][_word] += _lr     #正确的类别
                        err += 1    #错误的数量+1
                    total += 1      #总条数+1
            print("Epoch %d: %.2f" % (_epoch, (float(total - err) / total *100.0)))#返回每轮预测的准确率,转化为百分数
            
    return model
 
def save(modin, frs):
    
    with open(frs, "w") as f:
        dump(modin, f) #用dump方法向文件写str
        

if __name__=="__main__":
	seed(408) #固定随机数种子
    save(train(*sys.argv[1:3], build_model(*sys.argv[1:3])),sys.argv[3])
    #将第一个参数和第二个参数(训练集句子和分类)给build_model后,将句子、标签和返回的词典传入train中,最后保存模型

我们可以使用tqdm Python进度条来展示演示过程。在命令行执行:

:~/nlp/tnews$ python learnw.py src.train.bpe.txt tgt.train.s.txt learnw.model.txt

在这里插入图片描述
执行到100轮左右,准确率就在98%左右震荡。

对验证集做验证与早停

使用前面我们写好的脚本,在命令行输入:

:~/nlp/tnews$ python predict.py src.dev.bpe.txt learnw.model.txt pred.learnw.dev.txt
:~/nlp/tnews$ python acc.py pred.learnw.dev.txt tgt.dev.s.txt 
46.92

可以看到,准确率是46.92%,在验证集上表现一般。与我们前面提到的统计方法相比,可以大致认为是统计方法最优化的最高准确率。

我们找到的参数是在训练集上表现最好的,但未必是在所有情况下表现最好的。我们需要的模型是在验证集上性能最好的,因此我们需要在模型对验证集的表现上做修改:

#encoding: utf-8

def eva(srcvf, tgtvf, model):#采集对验证集的正确率
    
    total = corr = 0
    with open(srcvf,"rb") as fsrc, open(tgtvf,"rb") as ftgt:
         for sline, tline in zip(fsrc, ftgt):#对每行数据
                _s, _t = sline.strip(), tline.strip()
                if _s and _t:
                    _s, _class = _s.decode("utf-8").split(), _t.decode("utf-8")
                    max_class = compute_instance(model, _s)[1]  #仅需要预测的最大类别
                    if max_class == _class:
                        corr += 1
                    total += 1
    return float(corr) / total * 100.0        #返回每轮验证集的准确率         
                                        
def train(srcf, tgtf, srcvf, tgtvf, model, frs, base_lr=0.1, max_run=128):#学习率初始为0.1,训练128轮
    
    with open(srcf,"rb") as fsrc, open(tgtf,"rb") as ftgt:
        _best_acc = eva(srcvf, tgtvf, model)
        print("Epoch 0: dev %.2f" % _best_acc)  #获得每轮中验证集上的准确率
        for _epoch in range(1,max_run+1): #在每轮中
            fsrc.seek(0)    #对文件做复位,每次读取文件开头
            ftgt.seek(0)
            _lr = base_lr / sqrt(_epoch) #将学习率不断变小,控制步长越来越小
            total = err = 0
            for sline, tline in zip(fsrc, ftgt):#对每行数据
                _s, _t = sline.strip(), tline.strip()
                if _s and _t:
                    _s, _class = _s.decode("utf-8").split(), _t.decode("utf-8")#_s为子词,_t为类别
                    scores, max_class, max_score = compute_instance(model, _s)#算出每行中每个类别的分数、最大分数的类别、最大的分数
                    if max_class != _class:#判断最大的类别和标准答案的类别是否相等
                        for _word in _s:
                            model[max_class][_word] -= _lr  #错误的类别
                            model[_class][_word] += _lr     #正确的类别
                        err += 1    #错误的数量+1
                    total += 1      #总条数+1
            _eva_acc = eva(srcvf, tgtvf, model)
            print("Epoch %d: train %.2f, dev %.2f" % (_epoch, (float(total - err) /
            total *100.0), _eva_acc))   #返回每轮训练集、验证集的准确率,转化为百分数
            if _eva_acc >= _best_acc:   #如果当前在验证集的准确率是最好的,就保存这个模型,并更新最好准确率
                save(model, frs)
                _best_acc = _eva_acc
            
    return model
 
def save(modin, frs):
    
    with open(frs, "w") as f:
        dump(modin, f) #用dump方法向文件写str
        

if __name__=="__main__":
	seed(408) #固定随机数种子
    train(*sys.argv[1:5], build_model(*sys.argv[1:3]),sys.argv[5])
    #传入训练句子、训练标签、验证句子、验证标签以及保存模型文件

在命令行执行:

:~/nlp/tnews$ python learnw.py src.train.bpe.txt tgt.train.s.txt src.dev.bpe.txt tgt.dev.s.txt learnw.model.txt 

在这里插入图片描述
我们可以看到,对验证集的测试上,在很早的轮次已经趋于稳定值了。因此我们没有必要跑完所有128轮,对验证集的准确率的提升没有意义。
我们引入early_stop参数,作为早停的停止条件。如果我们连续early_stop轮都没有找到更好的结果,我们就将训练停止 ,避免无效的训练:

if _eva_acc >= _best_acc:   #如果当前在验证集的准确率是最好的,就保存这个模型,并更新最好准确率
	save(model, frs)
    _best_acc = _eva_acc
    anbest = 0
else:
    anbest += 1
    if anbest > earlystop:	#如果连续earlystop轮准确率都没有提高,则停止训练
    break  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/722132.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据库大作业——音乐平台数据库管理系统

W...Y的主页&#x1f60a; 代码仓库分享&#x1f495; 《数据库系统》课程设计 &#xff1a;流行音乐管理平台数据库系统&#xff08;本数据库大作业使用软件sql server、dreamweaver、power designer&#xff09; 目录 系统需求设计 数据库概念结构设计 实体分析 属性分…

Redis小对象压缩

小对象压缩存储 如果Redis内部管理的集合数据结构很小&#xff0c;他会使用紧凑存储形式压缩存储。 Redis的ziplist是一个紧凑的字节数组结构&#xff0c;如下图所示&#xff0c;每个元素之间都是紧挨着的。 如果他存储的是hash结构&#xff0c;那么key和value会作为两个ent…

Arcgis导入excel出现的问题

我手动添加了object-id字段也没有用&#xff0c;然后再excel里面又添加了一行&#xff0c;关闭后打开还是不行&#xff0c;额案后在网上看到了一种方法&#xff0c;很有效&#xff0c;予以记录。 1、我的文件是csv格式&#xff0c; 先在excel里面另存为xlsx格式 2、转换工具里…

【机器学习】对大规模的文本数据进行多标签的分类处理

1. 引言 1.1. NLP研究的背景 随着人工智能技术的飞速发展&#xff0c;智能助手、聊天机器人和虚拟客服的需求正呈现出爆炸性增长。这些技术不仅为人们提供了极大的生活便利&#xff0c;如日程管理、信息查询和情感陪伴&#xff0c;还在工作场景中显著提高了效率。聊天机器人凭…

阿里又出AI神器,颠覆传统图像编辑,免费开源!

文章首发于公众号&#xff1a;X小鹿AI副业 大家好&#xff0c;我是程序员X小鹿&#xff0c;前互联网大厂程序员&#xff0c;自由职业2年&#xff0c;也一名 AIGC 爱好者&#xff0c;持续分享更多前沿的「AI 工具」和「AI副业玩法」&#xff0c;欢迎一起交流~ 最近阿里开源了 Mi…

【云手机】数据安全如何保障?

安全办公&#xff0c;信息安全&#xff0c;这是企业使用云手机的初衷和目的&#xff0c;云手机在数据保密&#xff0c;远程办公等功能上有巨大的优势&#xff0c;也为企业提供了支持 首先就是云手机能够实现数据的集中管理和加密存储。所有办公相关的数据都存储在云端的安全服务…

ES 8.14 Java 代码调用,增加knnSearch 和 混合检索 mixSearch

1、pom依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-client</artifactId><version>8.14.0</version></dependency><dependency><groupId>co.elastic.clients<…

Vue62-$nextTick和$event

一、$nextTick 1-1、需求 点击编辑按钮&#xff0c;文本框自动获取焦点。 没有生效&#xff01;因为vue是将function中的代码执行完&#xff0c;再重新解析模版&#xff0c;所以存在时间上的问题。 解决方式一&#xff1a;使用定时器 解决方式二&#xff1a;$nextTick $nextT…

TikTok账号养号的流程分享

对于很多刚开始运营TikTok的新手小白来说&#xff0c;都会有一个同样的疑问&#xff0c;那就是&#xff1a;TikTok到底需不需要养号&#xff1f;这里明确告诉大家是需要养号的&#xff0c;今天就把我自己实操过的养号经验和策略总结出来&#xff0c;分享给大家。 一、什么是Ti…

配置OSPF认证(华为)

#交换设备 配置OSPF认证-基于华为路由器 OSPF&#xff08;开放最短路径优先&#xff09;是一种内部网关协议&#xff08;IGP&#xff09;&#xff0c;用于在单一自治系统&#xff08;AS&#xff09;内决策路由。OSPF认证功能是路由器中的一项安全措施&#xff0c;它的主要用途…

uniapp顶部导航栏实现自定义功能按钮+搜索框并监听响应事件

目录 第一步&#xff1a;先下载按钮需要展示的图标&#xff08;若不使用图标&#xff0c;直接使用文字可跳过这步&#xff09; 1、点击需要的图标&#xff0c;添加入库 2、点击旁边的购物车&#xff0c;在弹出的窗口中选择下载代码 3、解压下载的压缩包&#xff0c;将这几个…

SpringMVC的使用

SpringMVC详情 RequestMapping("/hello") 负责用户的请求路径与后台服务器之间的映射关系 如果请求路径不匹配,则用户报错404 ResponseBody 作用: 将服务器的返回值转化为JSON. 如果服务器返回的是String类型,则按照自身返回. 新增: post请求类型 PostMapping("…

【anaconda】本地永久设置镜像源

【anaconda】本地永久设置镜像源 可以通过命令行设置全局的 pip 配置&#xff1a; pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

支付宝直付通 VS 微信收付通分账产品异同

随着电子商务的蓬勃发展&#xff0c;支付分账产品成为了各大平台不可或缺的一部分。在众多支付分账产品中&#xff0c;支付宝和微信无疑是两大巨头。通过我们公司项目的实际使用经验&#xff0c;我们将从多个维度对这两款产品进行全方位的比较&#xff0c;帮助大家更直观地了解…

【AI基础】租用云GPU之autoDL部署大模型ollama+llama3

在这个显卡昂贵的年代&#xff0c;很多想要尝试一下AI的人可能都止步于第一步。这个时候我们可以租用在线的GPU资源来使用AI。autoDL就是这样的一个云平台。 一、创建服务器 1.1 注册账号 官网&#xff1a;https://www.autodl.com/ | 租GPU就上AutoDL 帮助文档&#xff1a;…

获取天气预报

目录 一 设计原型 二 后台源码 一 设计原型 二 后台源码 using Newtonsoft.Json; using System.Net; using System.Text;namespace 获取天气预报 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private void button1_Click(object sender, Eve…

重学java 73.设计模式

本想送你一本沉思录&#xff0c;可该迷途知返的人是我 —— 24.6.18 设计模式 设计模式(Design pattern)&#xff0c;是一套被反复使用、经过分类编目的、代码设计经验的总结&#xff0c;使用设计模式是为了可重用代码、保证代码可靠性、程序的重用性,稳定性。 1995 年&#x…

贪心算法——赶作业(C++)

慢慢来&#xff0c;沉稳一点。 2024年6月18日 题目描述 A同学有n份作业要做&#xff0c;每份作业有一个最后期限&#xff0c;如果在最后期限后交作业就会扣分&#xff0c;现在假设完成每份作业都需要一天。A同学想安排作业顺序&#xff0c;把扣分降到最低&#xff0c;请帮他实…

注册安全分析报告:PingPong

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞 …

C语言中的内存动态管理

1.为什么有动态内存管理 int a20;//开辟4个字节 int arr[10]{0};//开辟40个字节 上述的代码有两个特点 1.开辟空间的大小是固定的。 2.数组在申明的时候已经固定了大小&#xff0c;无法更改。 这样写代码不够灵活&#xff0c;所以c语言中引入了动态内存管理&#xff0c;让程序…