【Andrej Karpathy 神经网络从Zero到Hero】--2.语言模型的两种实现方式 (Bigram 和 神经网络)

目录

  • 统计 Bigram 语言模型
    • 质量评价方法
  • 神经网络语言模型

【系列笔记】
【Andrej Karpathy 神经网络从Zero到Hero】–1. 自动微分autograd实践要点

本文主要参考 大神Andrej Karpathy 大模型讲座 | 构建makemore 系列之一:讲解语言建模的明确入门,演示

  1. 如何利用统计数值构建一个简单的 Bigram 语言模型
  2. 如何用一个神经网络来复现前面 Bigram 语言模型的结果,以此来展示神经网络相对于传统 n-gram 模型的拓展性。

统计 Bigram 语言模型

首先给定一批数据,每个数据是一个英文名字,例如:

['emma','olivia','ava','isabella','sophia','charlotte','mia','amelia','harper','evelyn']

Bigram语言模型的做法很简单,首先将数据中的英文名字都做成一个个bigram的数据

其中每个格子中是对应的二元组,eg: “rh” ,在所有数据中出现的次数。那么一个自然的想法是对于给定的字母,取其对应的行,将次数归一化转成概率值,然后根据概率分布抽取下一个可能的字母:

g = torch.Generator().manual_seed(2147483647)
P = N.float() # N 即为上述 counts 矩阵
P = P / P.sum(1, keepdims=True) # P是每行归一化后的概率值for i in range(5):out = []ix = 0  ## start符和end符都用 id=0 表示,这里是startwhile True:p = P[ix] # 当前字符为 ix 时,预测下一个字符的概率分布,实质是一个多项分布(即可能抽到的值有多个,eg: 掷色子是六项分布)ix = torch.multinomial(p, num_samples=1, replacement=True, generator=g).item()out.append(itos[ix])if ix == 0: ## 当运行到end符,停止生成breakprint(''.join(out))

输出类似于:

mor.
axx.
minaymoryles.
kondlaisah.
anchshizarie.

质量评价方法

我们还需要方法来评估语言模型的质量,一个直观的想法是:
P ( s 1 s 2 . . . s n ) = P ( s 1 ) P ( s 2 ∣ s 1 ) ⋯ P ( s n ∣ s n − 1 ) P(s_1s_2...s_n) = P(s_1)P(s_2|s_1)\cdots P(s_n|s_{n-1}) P(s1s2...sn)=P(s1)P(s2s1)P(snsn1)
但上述计算方式有一个问题,概率值都是小于1的,当序列的长度比较长时,上述数值会趋于0,计算时容易下溢。因此实践中往往使用 l o g ( P ) log(P) log(P)来代替,为了可以对比不同长度的序列的预测效果,再进一步使用 l o g ( P ) / n log(P)/n log(P)/n 表示一个序列平均的质量

上述统计 Bigram 模型在训练数据上的平均质量为:

log_likelihood = 0.0
n = 0for w in words: # 所有word里的二元组概率叠加chs = ['.'] + list(w) + ['.']for ch1, ch2 in zip(chs, chs[1:]):ix1 = stoi[ch1]ix2 = stoi[ch2]prob = P[ix1, ix2]logprob = torch.log(prob)log_likelihood += logprobn += 1 # 所有word里的二元组数量之和nll = -log_likelihood
print(f'{nll/n}') ## 值为 2.4764,表示前面做的bigram模型,对现有训练数据的置信度## 这个值越低表示当前模型越认可训练数据的质量,而由于训练数据是我们认为“好”的数据,因此反过来就说明这个模型好

但这里有一个问题是,例如:

log_likelihood = 0.0
n = 0#for w in words:
for w in ["andrejz"]:chs = ['.'] + list(w) + ['.']for ch1, ch2 in zip(chs, chs[1:]):ix1 = stoi[ch1]ix2 = stoi[ch2]prob = P[ix1, ix2]logprob = torch.log(prob)log_likelihood += logprobn += 1print(f'{ch1}{ch2}: {prob:.4f} {logprob:.4f}')print(f'{log_likelihood=}')
nll = -log_likelihood
print(f'{nll=}')
print(f'{nll/n}')

输出是

.a: 0.1377 -1.9829
an: 0.1605 -1.8296
nd: 0.0384 -3.2594
dr: 0.0771 -2.5620
re: 0.1336 -2.0127
ej: 0.0027 -5.9171
jz: 0.0000 -inf
z.: 0.0667 -2.7072
log_likelihood=tensor(-inf)
nll=tensor(inf)
inf

可以发现由于,jz 在计数矩阵 N 中为0,即数据中没有出现过,导致 log(loss) 变成了负无穷,这里为了避免这样的情况,需要做 平滑处理,即 P = N.float() 改成 P = (N+1).float(),这样上述代码输出变成:

.a: 0.1376 -1.9835
an: 0.1604 -1.8302
nd: 0.0384 -3.2594
dr: 0.0770 -2.5646
re: 0.1334 -2.0143
ej: 0.0027 -5.9004
jz: 0.0003 -7.9817
z.: 0.0664 -2.7122
log_likelihood=tensor(-28.2463)
nll=tensor(28.2463)
3.5307815074920654

避免了出现 inf 这种数据溢出问题。


神经网络语言模型

接下来尝试用神经网络的方式构建上述bigram语言模型:

# 构建训练数据
xs, ys = [], [] # 分别是前一个字符和要预测的下一个字符的id
for w in words[:5]:chs = ['.'] + list(w) + ['.']for ch1, ch2 in zip(chs, chs[1:]):ix1 = stoi[ch1]ix2 = stoi[ch2]print(ch1, ch2)xs.append(ix1)ys.append(ix2)    xs = torch.tensor(xs)
ys = torch.tensor(ys)
# 输出示例:. e
#          e m
#          m m
#          m a
#          a .
#       xs: tensor([ 0,  5, 13, 13,  1])
#       ys: tensor([ 5, 13, 13,  1,  0])# 随机初始化一个 27*27 的参数矩阵
g = torch.Generator().manual_seed(2147483647)
W = torch.randn((27, 27), generator=g, requires_grad=True) # 基于正态分布随机初始化
# 前向传播
import torch.nn.functional as F
xenc = F.one_hot(xs, num_classes=27).float() # 将输入数据xs做成one-hot embedding
logits = xenc @ W # 用于模拟统计模型中的统计数值矩阵,由于 W 是基于正态分布采样,logits 并非直接是计数值,可以认为是 log(counts)
## tensor([[-0.5288, -0.5967, -0.7431,  ...,  0.5990, -1.5881,  1.1731],
##        [-0.3065, -0.1569, -0.8672,  ...,  0.0821,  0.0672, -0.3943],
##        [ 0.4942,  1.5439, -0.2300,  ..., -2.0636, -0.8923, -1.6962],
##        ...,
##        [-0.1936, -0.2342,  0.5450,  ..., -0.0578,  0.7762,  1.9665],
##        [-0.4965, -1.5579,  2.6435,  ...,  0.9274,  0.3591, -0.3198],
##        [ 1.5803, -1.1465, -1.2724,  ...,  0.8207,  0.0131,  0.4530]])
counts = logits.exp() # 将 log(counts) 还原成可以看作是 counts 的矩阵
## tensor([[ 0.5893,  0.5507,  0.4756,  ...,  1.8203,  0.2043,  3.2321],
##        [ 0.7360,  0.8548,  0.4201,  ...,  1.0856,  1.0695,  0.6741],
##        [ 1.6391,  4.6828,  0.7945,  ...,  0.1270,  0.4097,  0.1834],
##        ...,
##        [ 0.8240,  0.7912,  1.7245,  ...,  0.9438,  2.1732,  7.1459],
##        [ 0.6086,  0.2106, 14.0621,  ...,  2.5279,  1.4320,  0.7263],
##        [ 4.8566,  0.3177,  0.2802,  ...,  2.2722,  1.0132,  1.5730]])
probs = counts / counts.sum(1, keepdims=True) # 用于模拟统计模型中的概率矩阵,这其实即是 softmax 的实现
loss = -probs[torch.arange(5), ys].log().mean() # loss = log(P)/n, 这其实即是 cross-entropy 的实现

接下来可以通过loss.backward()来更新参数 W:

for k in range(100):# forward passxenc = F.one_hot(xs, num_classes=27).float() logits = xenc @ W # predict log-countscounts = logits.exp()probs = counts / counts.sum(1, keepdims=True) loss = -probs[torch.arange(num), ys].log().mean() + 0.01*(W**2).mean() ## 这里加上了L2正则,防止过拟合print(loss.item())# backward passW.grad = None # 每次反向传播前置为Noneloss.backward()# updateW.data += -50 * W.grad  

注意这里 logits = xenc @ W 由于 xenc 是 one-hot 向量,因此这里 logits 相当于是抽出了 W 中的某一行,而结合 bigram 模型中,loss 实际上是在计算实际的 log(P[x_i, y_i]),那么可以认为这里 W 其实是在拟合 bigram 中的计数矩阵 N(不过实际是 logW 在拟合 N)

另外上述神经网络的 loss 最终也是达到差不多 2.47 的最低 loss。这是合理的,因为从上面的分析可知,这个神经网络是完全在拟合 bigram 计数矩阵的,没有使用更复杂的特征提取方法,因此效果最终也会差不多。

这里 loss 中还加了一个 L2 正则,主要目的是压缩 W,使得它向全 0 靠近,这里的效果非常类似于 bigram 中的平滑手段,想象给一个极大的平滑:P = (N+10000).float()`,那么 P 会趋于一个均匀分布,而 W 全为 0 会导致 counts = logits.exp() 全为 1,即也在拟合一个均匀分布。这里前面的参数 0.01 即是用来调整平滑强度的,如果这个给的太大,那么平滑太大了,就会学成一个均匀分布(当然实际不会希望这样,所以不会给很大)

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

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

相关文章

(二 十 二)趣学设计模式 之 备忘录模式!

目录 一、 啥是备忘录模式?二、 为什么要用备忘录模式?三、 备忘录模式的实现方式四、 备忘录模式的优缺点五、 备忘录模式的应用场景六、 总结 🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,…

安装SPSS后启动显示应用程序无法启动,因为应用程序的并行配置不正确的解决方案

软件安装报错问题有需要远程文章末尾获取联系方式,可以帮你远程处理各类安装报错。 一、安装SPSS后启动显示应用程序无法启动,因为应用程序的并行配置不正确报错 在成功安装 SPSS 软件后,尝试启动应用程序时,系统弹出错误提示窗…

IP,MAC,ARP 笔记

1.什么是IP地址 IP 地址是一串由句点分隔的数字。IP 地址表示为一组四个数字,比如 192.158.1.38 就是一个例子。该组合中的每个数字都可以在 0 到 255 的范围内。因此,完整的 IP 寻址范围从 0.0.0.0 到 255.255.255.255。 IP 地址不是随机的。它们由互…

C++11中的Condition_variable

C11中的condition_variable 在C11中,条件变量(std::condition_variable)是线程同步机制之一,用于在多线程环境中实现线程间的通信和协调。它允许一个或多个线程在某个条件尚未满足时等待,直到其他线程通知条件已经满足…

IO多路复用实现并发服务器

一.select函数 select 的调用注意事项 在使用 select 函数时,需要注意以下几个关键点: 1. 参数的修改与拷贝 readfds 等参数是结果参数 : select 函数会直接修改传入的 fd_set(如 readfds、writefds 和 exceptfds&#xf…

_二级继电器程控放大倍数自动设置

简介 在开发项目中,有时会遇到需要使用程控放大的情况,如果没有opa那种可编程放大器,那么就需要通过继电器来控制放大倍数。而在继电器程控中,常用的是二级程控,三级程控相较于二级就复杂了许多。 在二级程控中&#x…

电脑总显示串口正在被占用处理方法

1.现象 在嵌入式开发过程中,有很多情况下要使用串口调试,其中485/422/232转usb串口是非常常见的做法。 根据协议,接口芯片不同,需要安装对应的驱动程序,比如ch340,cp2102,CDM212364等驱动。可…

优雅拼接字符串:StringJoiner 的完整指南

在Java开发中,字符串拼接是高频操作。无论是日志格式化、构建CSV数据,还是生成动态SQL,开发者常需处理分隔符、前缀和后缀的组合。传统的StringBuilder虽然灵活,但代码冗余且易出错。Java 8推出的StringJoiner类,以简洁…

LabVIEW闭环控制系统硬件选型与实时性能

在LabVIEW闭环控制系统的开发中,硬件选型直接影响系统的实时性、精度与稳定性。需综合考虑数据采集速度(采样率、接口带宽)、计算延迟(算法复杂度、处理器性能)、输出响应时间(执行器延迟、控制周期&#x…

Hive的架构

1. 概念 Hive 是建立在 Hadoop 上的数据仓库工具,旨在简化大规模数据集的查询与管理。它通过类 SQL 语言(HiveQL)将结构化数据映射为 Hadoop 的 MapReduce,适合离线批处理,尤其适用于数据仓库场景。 2. 数据模型 表&a…

深入解析:Linux中KVM虚拟化技术

这篇文章将深入分析Linux中虚拟化技术的实现----KVM技术,从KVM技术的简介、技术架构、以及虚拟机和宿主机交互的重要处理逻辑出发,深入探究KVM技术的实现。 一、KVM简介: 首先,我们先查看一下KVM架构,看看它的整体工…

golang学习笔记——go语言安装及系统环境变量设置

文章目录 go语言安装go envgo getgoproxy测试安装 Go 插件安装 Go 插件依赖工具参考资料用户环境变量和系统环境变量用户环境变量系统环境变量示例设置环境变量的步骤设置用户环境变量设置系统环境变量 验证环境变量总结 2024年最火的5大Go框架1. Gin:高并发接口的“…

3.6c语言

#define _CRT_SECURE_NO_WARNINGS #include <math.h> #include <stdio.h> int main() {int sum 0,i,j;for (j 1; j < 1000; j){sum 0;for (i 1; i < j; i){if (j % i 0){sum i;} }if (sum j){printf("%d是完数\n", j);}}return 0; }#de…

【TI】如何更改 CCS20.1.0 的 WORKSPACE 默认路径

参考链接&#xff1a; 如何更改 CCS Theia 中工作区的默认位置&#xff1f;- Code Composer Studio 论坛 - Code Composer Studio™︎ - TI E2E 支持论坛 --- How to change the default location for the workspace in CCS Theia? - Code Composer Studio forum - Code Comp…

Vue3中动态Ref的魔法:绑定与妙用

前言 在Vue 3的开发过程中,动态绑定Ref是一项非常实用的技术,特别是在处理复杂组件结构和动态数据时。通过动态绑定Ref,我们可以更灵活地访问和操作DOM元素或组件实例,实现更高效的交互和状态管理。本文将详细介绍如何在Vue 3中实现动态Ref的绑定,并通过实例展示其妙用。…

CarPlanner:用于自动驾驶大规模强化学习的一致性自回归轨迹规划

25年2月来自浙大和菜鸟网络的论文“CarPlanner: Consistent Auto-regressive Trajectory Planning for Large-scale Reinforcement Learning in Autonomous Driving”。 轨迹规划对于自动驾驶至关重要&#xff0c;可确保在复杂环境中安全高效地导航。虽然最近基于学习的方法&a…

VS Code连接服务器教程

VS Code是什么 VS Code&#xff08;全称 Visual Studio Code&#xff09;是一款由微软推出的免费、开源、跨平台的代码编辑神器。VS Code 支持 所有主流操作系统&#xff0c;拥有强大的功能和灵活的扩展性。 官网&#xff1a;https://code.visualstudio.com/插件市场&#xff1…

【JavaWeb】Web基础概念

文章目录 1、服务器与客户端2、服务器端应用程序3、请求和响应4、项目的逻辑构成5、架构5.1 概念5.2 发展演变历程单一架构分布式架构 5.3 单一架构技术体系 6、本阶段技术体系 1、服务器与客户端 ①线下的服务器与客户端 ②线上的服务器与客户端 2、服务器端应用程序 我…

安徽省考计算机专业科目2025(持续更新)

目录 第一部分 计算机科学技术基础 第一章 计算机及其应用基础知识 1.1 计算机的特点、分类及其应用 1.2 信息编码与数据表示&#xff1b;数制及其转换方法&#xff1b;算术运算和逻辑运算的过程 第一部分 计算机科学技术基础 第一章 计算机及其应用基础知识 1.1 计算机…

前端知识点---路由模式-实例模式和单例模式(ts)

在 ArkTS&#xff08;Ark UI 框架&#xff09;中&#xff0c;路由实例模式&#xff08;Standard Instance Mode&#xff09;主要用于管理页面跳转。当创建一个新页面时&#xff0c;可以选择标准实例模式&#xff08;Standard Mode&#xff09;或单实例模式&#xff08;Single M…