NLP入门——复杂函数建模与链式求导

news2024/11/22 13:57:37

复杂函数建模

前面我们研究的梯度下降法分类,是简单的对每类中每个子词的分数进行求和,统计分数最大的类别并不断调整分数来提高准确率。
我们可以修改函数模型,用更加复杂的函数代替sum(),来达到更好的学习效果。

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

采用梯度下降法计算机进行预测时,我们的corpus中每一行数据对应15类都有一个分数,取分数最大的_max_class作为预测类别,返回的rs存放着每个类别的分数,相当于一个向量。由分析得出,在计算机中是通过函数来判断的。

表示层

之前我们每个词在每个类上初始化一个分数,也就是说用15个分数来表示一个词。
现在我们要更丰富的表示一个词,用一个向量k表示。k可以是很大的数 eg. k>100. 维度64

例如假设我们一共有8000个词,我们可以建立一个8000*64的矩阵,为每个单元初始化一个初值。则拼接成则个矩阵的每个行向量就代表这个子词。

特征生成:函数计算 (神经网络层)

根据已有的特征合并学习新的特征。例如一个人的特征:1.爱吃肉或菜;2.爱吃甜口或辣口。我们将特征1和2合并可以得到新的特征,能推断出更丰富信息例如他爱吃哪道菜。(爱吃菜and辣口----麻婆豆腐)

pytorch向量计算

用向量初始化权重与特征,做内积可以得到新的权重:

>>> import torch
>>> a = torch.randn(4)
>>> a
tensor([ 0.9045,  0.2413, -0.2746, -0.9162])
>>> b = torch.randn(4)
>>> (a*b).sum()		#a*b的和,做数乘
tensor(-2.1851)
>>> a*b
tensor([-1.3204, -0.3658, -0.2360, -0.2630])
>>> b.unsqueeze(1)	#将b转化为列向量
tensor([[-1.4598],
        [-1.5161],
        [ 0.8592],
        [ 0.2870]])
>>> a.unsqueeze(0)	#将a转化为行向量
tensor([[ 0.9045,  0.2413, -0.2746, -0.9162]])
>>> a.unsqueeze(0).mm(b.unsqueeze(1))	#求a和b的内积
tensor([[-2.1851]])	#结果为一个一维向量
>>> c=torch.randn(4)
>>> torch.cat([b.unsqueeze(1),c.unsqueeze(1)],dim=1)	#将b和c转化为列向量后拼接起来
tensor([[-1.4598,  0.3766],
        [-1.5161, -0.1238],
        [ 0.8592, -0.7182],
        [ 0.2870, -1.8328]])
>>> c.unsqueeze(1)
tensor([[ 0.3766],
        [-0.1238],
        [-0.7182],
        [-1.8328]])
>>> a.unsqueeze(0).mm(torch.cat([b.unsqueeze(1),c.unsqueeze(1)],dim=1))	#将行向量a分别与列向量b,c拼接起来的矩阵做乘法
tensor([[-2.1851,  2.1872]])
>>> (a*b).sum()
tensor(-2.1851)
>>> (a*c).sum()			#做数乘的值即为结果矩阵中每一项的值
tensor(2.1872)
>>> w = torch.randn(4,5)	#初始化4行5列的权重矩阵
>>> w
tensor([[ 0.6165,  0.8714,  1.8304, -2.2638,  0.8837],
        [-0.3679, -0.9885, -2.0797, -0.9804, -0.6806],
        [-1.4536,  1.0211, -0.4079, -0.4373, -0.9353],
        [-1.0958, -1.1655,  1.1883, -1.8312, -0.2542]])
>>> a.unsqueeze(0).mm(w)	#将a变成行向量与权重矩阵w做乘法就会得到五个特征
tensor([[ 1.8721,  1.3370,  0.1771, -0.4864,  1.1249]])

激活函数

如果我们输入的向量是v,构建出的矩阵L,为了提取多种特征,我们对其进行矩阵乘法运算,乘以不同的权重矩阵W_i:
L ∗ W 1 ∗ W 2 ∗ W 3 ∗ . . . ∗ W n L*W_1*W_2*W_3*...*W_n LW1W2W3...Wn
但由于乘法的结合律,上面的式子等价于
L ∗ W x , W x = W 1 ∗ W 2 ∗ W 3 ∗ . . . ∗ W n L*W_x,W_x=W_1*W_2*W_3*...*W_n LWx,Wx=W1W2W3...Wn
我们原本的n个变化W_1~W_n变为一个权重矩阵W_x,仅相当于做了一次变换,我们为了提取多重复杂特征而乘不同的权重矩阵的目的并没有实现。
为了解决由乘法的结合律带来的影响,我们使用非线性函数做激活函数,上面的式子变为:
a c t ( . . . a c t ( a c t ( a c t ( L ∗ W 1 ) ∗ W 2 ) ∗ W 3 ) ∗ . . . ∗ W n ) act(...act(act(act(L*W_1)*W_2)*W_3)*...*W_n) act(...act(act(act(LW1)W2)W3)...Wn)
常见的激活函数有 R e L U ReLU ReLU激活函数。 R e L U ReLU ReLU 函数的特点是,当输入小或等于0,则返回0,当输入大于0时,则返回输入值,公式如下所示。
r e l u ( x ) = { 0 , x ≤ 0 x , x   > 0 relu(x) = \begin{cases} 0, & \text{} x \leq 0 \\ x, & \text{} x \ >0 \end{cases} relu(x)={0,x,x0x >0在这里插入图片描述
r e l u ( x ) = m a x ( x , 0 ) relu(x) = max(x,0) relu(x)=max(x,0),则此函数中阈值为0发生变化。但如果我们想让函数发生偏移,使其阈值发生变化,则我们需要设置偏移量b_i:
a c t ( . . . a c t ( a c t ( a c t ( L ∗ W 1 + b 1 ) ∗ W 2 + b 2 ) ∗ W 3 + b 3 ) ∗ . . . ∗ W n + b n ) act(...act(act(act(L*W_1+b_1)*W_2+b_2)*W_3+b_3)*...*W_n+b_n) act(...act(act(act(LW1+b1)W2+b2)W3+b3)...Wn+bn)

>>> bias = torch.randn(5) #初始化偏差
>>> bias
tensor([ 1.1344, -0.1908, -1.1020,  0.2254, -1.5253])
>>> a.unsqueeze(0).mm(w)+bias #L*W_1+b_1
tensor([[ 1.0301,  0.5540, -1.7541, -0.0260, -2.1016]])
>>> (a.unsqueeze(0).mm(w)+bias).relu()
tensor([[1.0301, 0.5540, 0.0000, 0.0000, 0.0000]])

在调用relu()函数后,小于0的部分都被置为0,大于0则保留。

解析分类:分类器

假设有k个(k>10)向量,每个向量是64维的,在神经网络层可能会分为 n * 128 的向量,分类器作用是将128维的向量变换为15维(我们预测的文本共有15个类)。

>>> w2 = torch.randn(5,8)
>>> (a.unsqueeze(0).mm(w)+bias).relu().mm(w2)+torch.randn(8)
tensor([[-1.6464, -0.0626, -0.9157, -0.1282, -3.2316, -0.4933, -0.6772,  0.7208]])

我们若分为8类,则建立一个5x8的矩阵,并设置一个偏移量,长度应该与分类数一致。如上,得到的向量最大的一项是-0.0626,因此判定为第二类。

链式求导与反向传播

多元函数求导链式法则:
d f ( g ( x ) ) d x = d f ( g ( x ) ) g ( x ) ∗ d g ( x ) d x \frac{d f(g(x))}{dx} = \frac{df(g(x))}{g(x)}*\frac{dg(x)}{dx} dxdf(g(x))=g(x)df(g(x))dxdg(x)
则对于: a c t ( a c t ( a c t ( L ∗ W 1 + b 1 ) ∗ W 2 + b 2 ) ∗ W 3 + b 3 ) act(act(act(L*W_1+b_1)*W_2+b_2)*W_3+b_3) act(act(act(LW1+b1)W2+b2)W3+b3)
f ( g ( x ) ) = a c t ( L ∗ W 1 + b 1 ) f(g(x)) = act(L*W_1+b_1) f(g(x))=act(LW1+b1), h = g ( x ) = L ∗ W 1 + b 1 h=g(x)=L*W_1+b_1 h=g(x)=LW1+b1
d f ( h ) d x = d f ( h ) h ∗ d g ( x ) d x \frac{d f(h)}{dx} = \frac{df(h)}{h}*\frac{dg(x)}{dx} dxdf(h)=hdf(h)dxdg(x)

>>> a.unsqueeze(0).mm(w)+bias
tensor([[ 1.0301,  0.5540, -1.7541, -0.0260, -2.1016]])
>>> (a.unsqueeze(0).mm(w)+bias).relu()
tensor([[1.0301, 0.5540, 0.0000, 0.0000, 0.0000]])
>>> torch.tensor([1,1,0,0,0])
tensor([1, 1, 0, 0, 0])

最终tensor([1, 1, 0, 0, 0])即为 f ( h ) / h f(h)/h f(h)/h的导数值。

运算速度对比

我们对比Python单线程程序和pytorch运算向量数乘的时间差别:

#encoding: utf-8

from random import uniform
import torch
from time import time

vsize = 16384   #设置一维向量的长度
nrun = 1280     #设置算多少轮

a = [uniform(-1.0,1.0) for _ in range(vsize)] #初始化向量
b = [uniform(-1.0,1.0) for _ in range(vsize)]

c = torch.tensor(a) #初始化tensor张量
d = torch.tensor(b)

def dot(a, b):  #求a与b的内积,python实现
    _s = 0.0
    for au, bu in zip(a,b):
        _s += au * bu
    return _s
 
_st = time()
for _ in range(nrun):
    rs = dot(a,b)
_et = time()
print("Python运算时间:%f"%(_et - _st))
 
_st = time()
for _ in range(nrun):
    rs = c.dot(d)
_et = time()
print("pytorch运算时间:%f"%(_et - _st))
:~/nlp/tnews$ python test_torch.py 
Python运算时间:0.434339
pytorch运算时间:0.007864

我们可以看到,时间相差50倍左右,因此用pytorch进行向量运算速度明显更快。

向量a乘m* n矩阵速度优于乘n个m*1列向量

#encoding: utf-8

from random import uniform
import torch
from time import time

bsize = 1
vsize = 512     
osize = 768
nrun = 1280

a = torch.randn(bsize,vsize)    #a为1*512向量
w = torch.randn(vsize,osize)    #w为512*768向量
w1 = w.narrow(1, 0, osize // 2) #w1为w的前半部分矩阵
w2 = w.narrow(1, osize // 2, osize // 2) #w2为后半部分
#narrow(dim,start,length) 传参均为整数,分别代表(维度,起始索引,区间长度)
#a*w结果为bsize * osize 的矩阵

_st = time()
for _ in range(nrun): 
    _rs = a.mm(w)               #a乘以矩阵(一起乘)
_et = time()
print(_et - _st)
 
_st = time()
for _ in range(nrun):           #a分别乘以前半个、后半个矩阵
    _rs = a.mm(w1) 
    _rs = a.mm(w2)
_et = time()
print(_et - _st)
:~/nlp/tnews$ python test_batch.py 
合并算时间:0.023270
分开算时间:0.060520

我们可以看到合并计算速度明显优于分开计算。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LLM大语言模型应用方案之RAG检索增强生成的实现步骤。

0.我理解的RAG 什么是RAG? RAG的全称是“检索增强生成模型”(Retrieval-Augmented Generation)。这是一种特别聪明的大语言模型。 RAG是怎么工作的呢? 1.检索:当你问RAG一个问题时,它会先去“图书…

Dockerfile封装制作pytorch(tensorflow)深度学习框架 + jupyterlab服务 + ssh服务镜像

一:docker-hub官网寻找需求镜像 1.我们在https://hub.docker.com/官网找到要封装的pytorch基础镜像,这里我们以pytorch1.13.1版本为例 2.我们找到的这个devel版本的镜像(我们需要cuda的编译工具) pytorch版本是1.13.1,…

Windows桌面运维----第四天

1、U盘故障打不开: 操作方式:WinR打开运行,输入cmd确定,在(C:\Users\Administrator>)后输入chkdsk,空格,输入U盘盘符,例如F:/F,回车,等待修复完成。 2、…

Bad return type in lambda expression: void cannot be converted to Unit

1.背景 当kotlin中声明需要传入一个返回值为空(Unit)的方法引用. 代码示例: 下方第二个参数为一个方法引用,(参数为BiometricPrompt.AuthenticationResult, 返回类型为Unit). object BiometricPromptUtil {private const val TAG "BiometricPromptUtil"fun cre…

Day6—热点搜索词统计

一、要求 根据用户上网的搜索记录对每天的热点搜索词进行统计,以了解用户所关心的热点话题。 要求完成:统计每天搜索数量前3名的搜索词(同一天中同一用户多次搜索同一个搜索词视为1次)。 二、数据 三、配置scala环境 1.下载sca…

思科PAP命令笔记

命令格式 含义 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/qq_44862120/article/details/115675197 ——————…

动态规划——活动安排问题II(C++)

Take it easy! 2024年6月19日 题目描述 假设有n个活动和一个资源,每个活动执行时都需要占用该资源,并且该资源在任何时间只能被一个活动所占用,一旦某个活动开始执行,中间将不能被打断,直到其执行完毕。每个活动i都有…

数据结构--顺序表(图文)

顺序表的概念和特点 顺序表是一种线性数据结构,它由一组数据元素构成,这些元素具有相同的特性,并按照一定的顺序排列。在顺序表中,数据元素通常存储在连续的内存空间中,这使得通过索引可以直接访问到表中的任意元素。…

考研计组chap2数据的表示和运算

3一、进位计数制 1.r进制 第i位表示r进制的权为i 2.进制转换 (1)r->10 对应位置数*权值 (2)2 -> 16 or 8 每三位2进制数可表示1位16进制 每四位2进制数可表示1位16进制 so 分开之后转为16进制即可 eg:1…

欧拉函数的求解

欧拉函数的定义 欧拉函数的性质 性质1是性质2的特殊情况 性质1的理解:一个数a是质数,前面的数b与a的gcd一定是1 性质2的理解:1,2,…p,p1,p2…2p,…3p…p^k 其中以np结尾的序列重复了p^(k-1)次,每一次的循环…

Day10—Spark SQL基础

Spark SQL介绍 ​ Spark SQL是一个用于结构化数据处理的Spark组件。所谓结构化数据,是指具有Schema信息的数据,例如JSON、Parquet、Avro、CSV格式的数据。与基础的Spark RDD API不同,Spark SQL提供了对结构化数据的查询和计算接口。 Spark …

星戈瑞FITC-Cytochrome C:荧光标记细胞色素C的研究与应用

细胞色素C(Cytochrome C)是一种位于线粒体内膜上的蛋白质。为了深入地研究细胞色素C在细胞生物学和病理学中的功能,科学家们常常采用荧光标记技术对其进行追踪和观察。其中,异硫氰酸荧光素(FITC)作为一种常…

iOS原生APP开发的技术难点

iOS原生APP开发的技术难点主要体现在以下几个方面,总而言之,iOS原生APP开发是一项技术难度较高的工作,需要开发者具备扎实的编程基础、丰富的开发经验和良好的学习能力。北京木奇移动技术有限公司,专业的软件外包开发公司&#xf…

再谈量化策略失效的问题

数量技术宅团队在CSDN学院推出了量化投资系列课程 欢迎有兴趣系统学习量化投资的同学,点击下方链接报名: 量化投资速成营(入门课程) Python股票量化投资 Python期货量化投资 Python数字货币量化投资 C语言CTP期货交易系统开…

数据结构之B树的理解与示例(C#)

文章目录 B树的基本概念与特点B树在C#数据结构中的应用创建一个B树节点的具体代码示例插入、删除和查找操作的示例B树在文件存储与处理中的具体应用示例总结 B树是一种自平衡的树数据结构,它维持数据的有序性,并且允许搜索、顺序访问、插入和删除的操作都…

前后端完整案例-简单模仿点点开黑抽奖

数据库 后台 源码:https://gitee.com/qfp17393120407/game 前台 源码: https://gitee.com/qfp17393120407/game-weeb vue项目打包 注意:打包时将IP改为自己公网IP npm run build公网页面 地址:点点模拟抽奖 进入页面抽奖…

电路笔记 :LM3481MM/NOPB升压模块,升压电路原理

LM3481MM/NOPB LM3481MM/NOPB 是德州仪器(Texas Instruments)的一款广泛应用的DC-DC控制器,常用于电源管理应用,特别是在需要升压(boost)、反激(flyback)、SEPIC或反向配置的场合。…

企业UDP文件传输工具测速的方式(下)

在前一篇文章中,我们深入讨论了UDP传输的基本概念和镭速UDP文件传输工具如何使用命令行快速进行速度测试。现在,让我们进一步探索更为高级和灵活的方法,即通过整合镭速UDP的动态或静态库来实现网络速度的测量,以及如何利用这一过程…

Java零基础之多线程篇:线程的多种创建方式

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一…

支持向量机 (SVM) 算法详解

支持向量机 (SVM) 算法详解 支持向量机(Support Vector Machine, SVM)是一种监督学习模型,广泛应用于分类和回归分析。SVM 特别适合高维数据,并且在处理复杂非线性数据时表现出色。本文将详细讲解 SVM 的原理、数学公式、应用场景…