李沐机器学习系列1--- 线性规划

1 Introduction

1.1 线性回归函数

典型的线性回归函数
f ( x ) = w ⃗ ⋅ x ⃗ f(x)=\vec{w} \cdot \vec{x} f(x)=w x
现实生活中,简单的线性回归问题很少,这里有一个简单的线性回归问题。房子的价格和房子的面积以及房子的年龄假设成线性关系。
p r i c e = w a r e a ∗ a r e a + w a g e + b price = w_{area}*area + w_{age}+b price=wareaarea+wage+b

1.2 损失函数

确定了变量和结果之间的大致关系以后,我们需要通过优化的方法进行迭代优化求解。
让我们从优化的角度来看这个问题。
c o s t = 0.5 ∑ i n ( w ⃗ ∗ x − y i ) T Q ( w ⃗ ∗ x − y i ) cost = 0.5\sum_i^n(\vec{w}*x-y^{i})^TQ(\vec{w}*x-y^{i}) cost=0.5in(w xyi)TQ(w xyi)
这是一个典型的QP问题,可以使用迭代的方法找到最优解。
在QP问题中,往往采用wolfe规则,但是这往往使用到全部的数据,在大规模的机器没有办法这么做,采用SGD的方法。

1.3 随机梯度下降

对于方程求解问题,典型的方法是牛顿迭代法。
基础的牛顿迭代法可以这么理解:
0 − f ( x i ) = f ′ ( x i ) ( x i + 1 − x i ) x i + 1 = x i − f ( x i ) f ′ ( x i ) \begin{aligned} 0-f(x_i) &=f'(x_i)(x_{i+1}-x_i) \\ x_{i+1}&=x_i-\frac{f(x_i)}{f'(x_i)} \end{aligned} 0f(xi)xi+1=f(xi)(xi+1xi)=xif(xi)f(xi)
对于这个符号,从直觉的角度理解,对于一个单调函数,如果f(x_i)和f’(x_i)要是同方向,就会离稳态越来越远,所以需要添加符号来保持稳态。使用简单的牛顿迭代法,参数会变化太大,所以在步长的选择上,有很多trick。
对于一个凸函数来说,梯度方向是让函数值升高的最快方向,对于一个非凸函数,梯度方向可能会导致求解进入局部最大值或者鞍点。
w ′ = w − λ f ′ ( x ) w ′ = w − λ β ∑ i ∈ β x ( i ) ( w T x ( i ) + b − y ( i ) ) b ′ = b − λ β ∑ i ∈ β ( w T x ( i ) + b − y ( i ) ) \begin{aligned} w' &=w-\lambda f'(x) \\ w' &= w-\frac{\lambda}{\beta}\sum_{i\in \beta}x^{(i)}(w^Tx^{(i)}+b -y^{(i)}) \\ b' & = b-\frac{\lambda}{\beta}\sum_{i\in \beta}(w^Tx^{(i)}+b -y^{(i)}) \\ \end{aligned} wwb=wλf(x)=wβλiβx(i)(wTx(i)+by(i))=bβλiβ(wTx(i)+by(i))

在这里插入图片描述

1.3 正态分布与平方损失

每个参数可能会有一定的噪声,噪声正态分布如下:
y = w ⃗ T x ⃗ + b + ϵ ϵ ∈ N ( 0 , σ 2 ) \begin{aligned} y &=\vec{w}^T\vec{x}+b+\epsilon \\ \epsilon& \in N(0, \sigma^2) \end{aligned} yϵ=w Tx +b+ϵN(0,σ2)

给定的x观察到特定的y的似然
p ( y ∣ x ) = 1 2 π σ 2 e x p ( − 1 2 σ 2 ( y − w ⃗ T x ⃗ − b ) 2 ) \begin{aligned} p(y|x)&=\frac{1}{\sqrt{2\pi \sigma^2}}exp(-\frac{1}{2\sigma^2}(y-\vec{w}^T\vec{x}-b)^2) \end{aligned} p(yx)=2πσ2 1exp(2σ21(yw Tx b)2)
每个点都是独立同分布
P ( y ∣ X ) = ∏ i = 1 n ( p ( y i ∣ x i ) ) P(y|X)=\prod_{i=1}^n(p(y^{i}|x^i)) P(yX)=i=1n(p(yixi))
进行对数
− l o g P ( y ∣ X ) = ∑ i = 1 n 0.5 l o g ( 2 π σ 2 ) + 1 2 σ 2 ( y i − w T x i − b ) 2 -logP(y|X)=\sum_{i=1}^n0.5log(2\pi \sigma^2)+\frac{1}{2\sigma^2}(y^i-w^Tx^i-b)^2 logP(yX)=i=1n0.5log(2πσ2)+2σ21(yiwTxib)2

2 线性回归从零开始实现

2.1 生成数据

def generate_data(w, b, num_examples):x = torch.norm(0, 1, (num_examples, len(w)))y = torch.matmul(w, x) + by += torch.norm(0, 0.01, y.shape())return x, y.reshape((-1, 1))

2.2 batch size data selector

def data_iter(features, labels, batch_size=10):num_examples = len(features)indices = list(range(num_examples))random.shuffle(indices)for i in range(0, num_examples, batch_size):batch_indices = torch.tensor(indices[i:min(i + batch_size, num_examples)])yield(features[batch_indices], labels[batch_indices]

2.3 定义网络和loss

sgd 和优化中的方法的区别,sgd利用部分样本,采用迭代的方式,进行优化;传统的优化方法,如qp或者sqp则对全部的样本的loss进行迭代,并且迭代过程中,会同时优化步长;

def linreg(x, w, b):y = torch.matmul(x, w) + breturn ydef loss(y_hat, y):l = 0.5 * (y - y_hat) ** 2return lw = torch.normal(0, 0.1, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
def sgd(params, lr, batch_size):with torch.no_grad():for param in params:param -= lr * param.grad / batch_sizeparam.grad.zero_()

一定要在参数更新这里将param.grad.zero_()

2.4 进行训练

num_epochs = 3
net = linreg
lr = 0.02
batch_size = 10
for epoch in range(num_epochs):for x, y in data_iter(features, labels, batch_size):l = loss(y, net(x, w, b))l.sum().backward()sgd([w, b], lr, batch_size) with torch.no_grad():predicted = net(features, w, b)mse = ((predicted - labels) ** 2).mean()print("epoch ", epoch, " MSE: ", mse.item())

3 使用pytorch进行简化

3.1 数据选择器

# 定义迭代器
def data_selector(data_arrays, batch_size, is_train=True):dataset = data.TensorDataset(*data_arrays)return data.DataLoader(dataset, batch_size, shuffle=is_train)batch_size = 10
data_iter = data_selector((features, labels), batch_size, True)

3.2 网络

# 定义各种函数
from torch import nn
net = nn.Sequential(nn.Linear(2, 1))
# 初始化数值
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
# 定义损失函数
loss = nn.MSELoss()
# 使用优化算法
trainer = torch.optim.SGD(net.parameters(), lr=0.03)

采用框架,参数被封装在了net中,在定义sgd训练,使用net.parameters()传递参数

3.3 训练

num_epochs = 3
for epoch in range(num_epochs):for x, y in data_iter:l = loss(net(x), y)trainer.zero_grad()l.sum().backward()trainer.step()l = loss(net(x), y)print(f'epoch {epoch}, loss {l: f}')

4 softmax

在这里插入图片描述
我们在机器学习中经常会遇到分类问题,每个种类都自己的预测结果。
o ⃗ = W ⃗ x ⃗ + b ⃗ \vec{o}=\vec{W}\vec{x}+\vec{b} o =W x +b

4.1 softmax运算

如果只是这样简单的全连接层计算,会产生一个问题,输出的累计和并不等于1。但是我们的标签的总和是1.
为了让总和为1,每项的输出范围为[0,1],并且使用exp求导的时候更加简单
s o f t m a x ( x ) = e x p ( o ⃗ i ) ∑ i = 0 n ( e x p ( o ⃗ i ) ) softmax(x)=\frac{exp(\vec{o}_i)}{\sum_{i=0}^n(exp(\vec{o}_i))} softmax(x)=i=0n(exp(o i))exp(o i)
我们在做决策的时候,输出最大概率项
在这里插入图片描述

4.2 损失函数

使用最大似然估计
− l o g P ( Y ∣ X ) = ∑ i = 1 n − l o g P ( y ( i ) ∣ x ( i ) ) = ∑ i = 1 n l ( y ( i ) , y ^ ( i ) ) -logP(Y|X)=\sum_{i=1}^n-logP(y^{(i)}|x^{(i)})=\sum_{i=1}^nl(y^{(i)},\hat{y}^{(i)}) logP(YX)=i=1nlogP(y(i)x(i))=i=1nl(y(i),y^(i))
理解不了,以后再说。


  • 知道真实概率的人所经历的惊异程度
    H [ P ] = ∑ j − P ( j ) l o g P ( j ) H[P]=\sum_j-P(j)logP(j) H[P]=jP(j)logP(j)

4.3 训练代码

  • 数据
# 使用pytorchvision 下载数据
# iterator
trans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)image, label = mnist_train[0]train_iter = data.DataLoader(mnist_train, 32, shuffle=True,num_workers=4)
test_iter = data.DataLoader(mnist_test, 32, shuffle=True,num_workers=4)
for x, y in train_iter:break
  • 定义网络结构
    这里要注意,pytorch上,采用nnum_inputs这种输入结构,输出结构1num_outputs
# 定义网络结构,输入结构是o=W*x+b, x是[n * 784], w = 784 * 10, b = 1 * 10
num_inputs = 784
num_outputs = 10
w = torch.normal(0, 0.01, (num_inputs, num_outputs), requires_grad=True)
b = torch.zeros(num_outputs, requires_grad=True)
print(w.shape, b.shape)
  • 定义softmax函数
# 定义关键函数softmax, 两个样本
x = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
y = torch.tensor([0, 2])
print(x[[0, 1], y])
# -log,这里的keepdim非常关键,这样才能保证两个数组的维度相同,可以对应上
def softmax(x):x_exp = torch.exp(x)partition = x_exp.sum(axis=1, keepdim=True)
#     print(partition.shape)return x_exp / partitionp_x = softmax(x)
print(p_x)
  • 定义loss
# 自定义模型
# 这里需要对x进行reshape,因为x很可能是乱的,需要reshape成n*num_inputs这种类型的
def net(x):return softmax(torch.matmul(x.reshape((-1, w.shape[0])), w) + b)# 定义损失函数
# 这里采用了小技巧,二维数组中,使用两个等长的向量,可以快捷的索引元素
def crossEntropy(y_hat, y):return -torch.log(y_hat[range(len(y_hat)), y])y = torch.tensor([0, 2])
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
crossEntropy(y_hat, y)
  • 评估训练中的精度
# 记录分类精度, 用argmax(y_hat)==y, 记录是否正确
def accuracy(y_hat, y):# 防止y_hat采用概率, 并且有多个类别if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:y_hat = y_hat.argmax(axis=1)cmp = y_hat.type(y.dtype) == yreturn float(cmp.type(y.dtype).sum())# 用总数和预测值进行比较
accuracy(y_hat, y) / len(y)#创建一个通用的评估器
class Accumulator:def __init__(self, n):self.data = [0.0] * n# 解决各种不同类型元素累加的trickdef add(self, *args):self.data = [a + float(b) for a, b in zip(self.data, args)]def reset(self):self.data = [0.0] * len(self.data)def __getitem__(self, i):return self.data[i]def evaluate_accuracy(net, data_iter):if isinstance(net, torch.nn.Module):net.eval()  # 将模型设置为评估模式metrics = Accumulator(2)with torch.no_grad():for x, y in data_iter:metrics.add(accuracy(net(x), y), y.numel())return metrics[0] / metrics[1]evaluate_accuracy(net, test_iter)
  • 进行训练
#进行训练
lr = 0.1
def updater(batch_size):return d2l.sgd((w, b), lr, batch_size)def train_epoch_ch3(net, train_iter, loss, updater):if isinstance(net, torch.nn.Module):net.train()metric = Accumulator(3)for x, y in train_iter:y_hat = net(x)l = loss(y_hat, y)if isinstance(updater, torch.optim.Optimizer):updater.zero_grad()l.mean().backward()updater.step()else:l.sum().backward()updater(x.shape[0])metric.add(l.sum(), accuracy(y_hat, y), y.numel())return metric[0] / metric[2], metric[1] / metric[2]
  • 其他内容
class Animator:  #@save"""在动画中绘制数据"""def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,ylim=None, xscale='linear', yscale='linear',fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,figsize=(3.5, 2.5)):# 增量地绘制多条线if legend is None:legend = []d2l.use_svg_display()self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)if nrows * ncols == 1:self.axes = [self.axes, ]# 使用lambda函数捕获参数self.config_axes = lambda: d2l.set_axes(self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)self.X, self.Y, self.fmts = None, None, fmtsdef add(self, x, y):# 向图表中添加多个数据点if not hasattr(y, "__len__"):y = [y]n = len(y)if not hasattr(x, "__len__"):x = [x] * nif not self.X:self.X = [[] for _ in range(n)]if not self.Y:self.Y = [[] for _ in range(n)]for i, (a, b) in enumerate(zip(x, y)):if a is not None and b is not None:self.X[i].append(a)self.Y[i].append(b)self.axes[0].cla()for x, y, fmt in zip(self.X, self.Y, self.fmts):self.axes[0].plot(x, y, fmt)self.config_axes()display.display(self.fig)display.clear_output(wait=True)
  • 进行训练
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):  #@save"""训练模型(定义见第3章)"""animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],legend=['train loss', 'train acc', 'test acc'])for epoch in range(num_epochs):train_metrics = train_epoch_ch3(net, train_iter, loss, updater)test_acc = evaluate_accuracy(net, test_iter)animator.add(epoch + 1, train_metrics + (test_acc,))train_loss, train_acc = train_metricsassert train_loss < 0.5, train_lossassert train_acc <= 1 and train_acc > 0.7, train_accassert test_acc <= 1 and test_acc > 0.7, test_accnum_epochs = 10
train_ch3(net, train_iter, test_iter, crossEntropy, num_epochs, updater)

4.4 softmax 的简洁实现

通过softmax 函数得到每项的概率时
y ^ j = e x p ( o j ) ∑ k e x p ( o k ) \hat{y}_j=\frac{exp(o_j)}{\sum_kexp(o_k)} y^j=kexp(ok)exp(oj)
为了避免太大的数,造成数据的上溢,将特别大的项减掉
y ^ j = e x p ( o j − m a x ( o k ) ) ∑ k e x p ( o k − m a x ( o k ) ) \hat{y}_j=\frac{exp(o_j - max(o_k))}{\sum_kexp(o_k-max(o_k))} y^j=kexp(okmax(ok))exp(ojmax(ok))
这样会造成某一些数会非常小,接近0,在计算交叉熵时, y i l o g y ^ i y_ilog\hat{y}_i yilogy^i会出现无穷小的情况,计算交叉熵时有一个小技巧
l o g ( y ^ i ) = o j − m a x ( o k ) − l o g ( ∑ k e x p ( o k − m a x ( o k ) ) ) log(\hat{y}_i)=o_j-max(o_k)-log(\sum_kexp(o_k-max(o_k))) log(y^i)=ojmax(ok)log(kexp(okmax(ok)))

使用pytorch 完整的代码实现

import torch
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoadertransform = transforms.ToTensor()
train_dataset = torchvision.datasets.MNIST(root='./data', train=True, transform=transform, download=True)
test_dataset = torchvision.datasets.MNIST(root='./data', train=False, transform=transform)batch_size = 256
train_iter = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_iter = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))
loss = nn.CrossEntropyLoss(reduction='mean')
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
num_epochs = 10for epoch in range(num_epochs):total_loss = 0.0  # 初始化总损失为零for x, y in train_iter:# 前向传播output = net(x)l = loss(output, y)# 反向传播trainer.zero_grad()l.backward()trainer.step()# 累加批次的损失total_loss += l.item()  # 使用 l.item() 将损失转换为 Python 数字# 计算整个 epoch 的平均损失average_loss = total_loss / len(train_iter)# 打印当前轮的平均损失print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {average_loss:.4f}')
Epoch [1/10], Loss: 1.0835
Epoch [2/10], Loss: 0.6061
Epoch [3/10], Loss: 0.5108
Epoch [4/10], Loss: 0.4644
Epoch [5/10], Loss: 0.4364
Epoch [6/10], Loss: 0.4164
Epoch [7/10], Loss: 0.4017
Epoch [8/10], Loss: 0.3903
Epoch [9/10], Loss: 0.3814
Epoch [10/10], Loss: 0.3728

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

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

相关文章

前端插件库-VUE3 使用 vue-codemirror 插件

VUE3 插件 vue-codemirror 使用步骤和实例、基于 CodeMirror &#xff0c;适用于 Vue 的 Web 代码编辑器。 第一步&#xff1a;安装 vue-codemirror & codemirror 包 &#xff0c; 以及语言包 npm install codemirror --save npm install vue-codemirror --savenpm insta…

JavaScript 基础二part2.语句

JavaScript 基础二part2.语句 2.1 表达式和语句表达式和语句的区别 程序三大流程控制语句2.2 分支语句1. if语句2、三元运算符3、switch 语句 2.3 循环结构2.3.1 断点调试2.3.2 while 循环循环退出简易ATM取款机案例2.3.3 for 循环退出循环案例 2.1 表达式和语句 表达式&#…

如何在win7同样支持Webview2 在 WPF 中使用本地 Webview2 ,如何不依赖系统 Runtime

项目运行环境&#xff1a; .Net Framework 4.5.2 Windows 7 x64 Service Pack 1 WebView2 Microsoft.WebView2.FixedVersionRuntime.120.0.2210.91.x64 考虑到很多老项目&#xff0c;本项目使用的是.Net Framework 4.5.2&#xff0c;.Net 更高版本的其实也是可以支持的。 …

2023最大技术潮:大模型冲击下的智能汽车

作者 |德新 编辑 |王博 过去这年最大的技术潮&#xff0c;非大模型莫属。 2023年初&#xff0c;由ChatGPT掀起的浪花&#xff0c;迅速地演变成了席卷全球的AI科技浪潮。汽车行业在其中也不可避免。各大车企纷纷投入与大模型相关的布局。 长城官宣成立了AI Lab&#xff0c;到…

2024年测试岗最新自动化测试面试题整理,干货满满

一、接口测试 1、接口测试用例的编写要点有哪些&#xff1f;&#xff08;问法二&#xff1a;接口测试用例设计需要考虑哪些方面&#xff1f;问法三&#xff1a;接口测试中有哪些要注意的测试点&#xff1f;&#xff09; 1&#xff09; 考虑输入参数和输出参数的合法性&#x…

了解并使用django-rest-framework-jwt

一 JWT认证 在用户注册或登录后&#xff0c;我们想记录用户的登录状态&#xff0c;或者为用户创建身份认证的凭证。我们不再使用Session认证机制&#xff0c;而使用Json Web Token&#xff08;本质就是token&#xff09;认证机制。 Json web token (JWT), 是为了在网络应用环…

本地套接字通信

本地套接字&#xff08;Unix域套接字&#xff09;是用于在同一台机器上的进程之间进行通信的一种方式。它不涉及网络协议&#xff0c;而是直接在文件系统中创建一个套接字文件&#xff0c;进程可以通过该文件进行通信。 本地套接字的作用&#xff1a;本地的进程间通信 有关系的…

交换机01_以太网

1、交换机工作原理 交换机是数据链路层的设备&#xff0c;数据链路层传输的是数据帧&#xff0c;所以封装的是MAC头部&#xff08;主要有源MAC地址和目的MAC地址&#xff09; 2、数据链路层的功能&#xff1a; 建立逻辑连接&#xff0c;进行物理地址寻址&#xff0c;差错校验…

odoo17 | 用户界面的基本交互

前言 现在我们已经创建了我们的新模型及其 相应的访问权限&#xff0c;是时候了 与用户界面交互。 在本章结束时&#xff0c;我们将创建几个菜单以访问默认列表 和窗体视图。 数据文件 &#xff08;XML&#xff09; Odoo在很大程度上是数据驱动的&#xff0c;因此模块定义的…

C++面向对象语法总结(一)

一、类 C中可以使用struct、class两个关键字来定义一个类struct和class的区别 struct的默认成员权限是publicclass的默认成员权限是private实际开发中&#xff0c;用class表示类的比较多&#xff0c;因为涉及到封装的思想 在函数中创建的对象&#xff0c;都是在栈空间&#xf…

【动态规划】LeetCode-10. 正则表达式匹配

10. 正则表达式匹配。 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。 ‘.’ 匹配任意单个字符‘*’ 匹配零个或多个前面的那一个元素 所谓匹配&#xff0c;是要涵盖 整个 字符串 s的&#xff0c;而不是部分字符串。 …

MySQL概述

M y S Q L 概述 \huge{MySQL概述} MySQL概述 MySQL学习笔记 引入 什么是数据库&#xff1f; D \color{red}D Data B \color{red}B Base&#xff08;DB&#xff09;&#xff0c;存储和管理数据的仓库。 使用的各种电子产品的网页&#xff0c;页面中的数据都是动态的&#xf…