1.安装
下面这个网址有具体安装步骤
【PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】】https://www.bilibili.com/video/BV1hE411t7RN?vd_source=1b9a0d7cc95ffe0bada9111b709b98a4
https://zh-v2.d2l.ai/chapter_installation/index.html
笔者用的是conda环境的pytorch,大家也记得装一下,讲解的代码都是基于pytorch的。
如果想要其他替换的代码,可以自主在上面链接书中找一下。
如果安装不懂的后台踢就行
2.预备知识
2.1数据操作
2.11.Tensor基础
(1)Tensor定义
中文名字叫做张量(可以视为多维数组)
基于 标量 向量 矩阵 来看张量,上述三个分别为零维,一维,二维张量
(2)GPU和CPU
- CPU:CPU更适合处理复杂逻辑和少量线程的计算任务。它拥有少量的核心,但每个核心都能处理复杂的任务和逻辑。在处理逻辑复杂、条件分支多的任务上,CPU具有优势。然而,在处理深度学习中大量并行计算任务时,CPU的效率相对较低。由于CPU并非专为大规模并行计算设计,其计算速度在处理这类任务时可能无法满足需求。
- GPU:GPU则专为并行计算而设计,拥有大量的小核心,这些小核心适合并行执行相似的任务。在深度学习中,GPU针对大量并行处理的任务(如矩阵乘法、卷积等)表现出色,可以大大缩短计算时间。GPU的并行计算能力在处理深度学习中的计算密集型任务时,通常远超CPU,能够提供更高的计算效率。
(3)Tensor数据类型
分为GPU和CPU两种变体
在PyTorch中,量化是一种将浮点计算转换为定点计算的技术,旨在减少模型的大小、加速推理过程,同时尽量保持模型的精度
以下是一些基本操作
(4)Tensor的创建
共享内存....
import torch
# 创建 形状为 2x3x4 的张量, 默认用 0 填充
t = torch.Tensor(2, 3, 4)
print(type(t)) # <class 'torch.Tensor'>
print(t.type()) # torch.FloatTensor
print(t.dtype) # torch.float32
print(t.size()) # torch.Size([2, 3, 4])
print(t.shape) # torch.Size([2, 3, 4])
print(t)
"""
tensor([[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]],
[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]]])
"""
# 使用预先存在的数据 (Python序列 或 numpy.ndarray) 创建张量
t = torch.Tensor([[1, 2, 3], [4, 5, 6]])
print(t.dtype) # torch.float32
print(t)
"""
tensor([[1., 2., 3.],
[4., 5., 6.]])
"""
(5)数学运算
import torch
t = torch.zeros((2, 3)) # size 可以以 序列的形式传入, 也可以以 *size 的形式传入
print(t, t.dtype)
"""
tensor([[0., 0., 0.],
[0., 0., 0.]]) torch.float32
"""
t0 = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.int8)
t = torch.ones_like(t0) # size 和 dtype 都保持与 t0 一致
print(t)
"""
tensor([[1, 1, 1],
[1, 1, 1]], dtype=torch.int8) torch.int8
"""
t = torch.full((3, 5), 100)
print(t)
"""
tensor([[100, 100, 100, 100, 100],
[100, 100, 100, 100, 100],
[100, 100, 100, 100, 100]])
"""
t = torch.eye(3, 5)
print(t)
"""
tensor([[1., 0., 0., 0., 0.],
[0., 1., 0., 0., 0.],
[0., 0., 1., 0., 0.]])
"""
t = torch.arange(0, 10, 1)
print(t)
"""
tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
"""
t = torch.linspace(0, 10, 5)
print(t)
"""
tensor([ 0.0000, 2.5000, 5.0000, 7.5000, 10.0000])
"""
t = torch.rand((1, 5))
print(t)
"""
tensor([[0.7706, 0.1781, 0.2407, 0.4579, 0.0864]])
"""
t = torch.randint(0, 9, (3, 5))
print(t)
"""
tensor([[3, 4, 8, 2, 7],
[5, 8, 7, 0, 7],
[0, 0, 8, 1, 8]])
"""
t = torch.randn((3, 6))
print(t)
"""
tensor([[ 0.9932, -1.1636, -0.3698, -0.6131, 0.0571, 0.6054],
[-0.5878, -0.1389, -1.6374, -0.2527, 0.3637, -0.3284],
[-0.9119, 0.3085, 0.8913, 0.9905, 0.6498, -0.7845]])
"""
(6)索引和切片
可以参考csdn中tensor基础讲解,在此就不罗嗦了
(7)连接cat
同上
(8)拆分chunk
import torch
t = torch.tensor([[1, 2, 3, 4], [5, 6, 7, 8]])
print(t.shape) # torch.Size([2, 5])
print(t)
"""
tensor([[1, 2, 3, 4],
[5, 6, 7, 8]])
"""
for x in torch.chunk(t, chunks=2, dim=0):
print(x)
"""
tensor([[1, 2, 3, 4]])
tensor([[5, 6, 7, 8]])
"""
for x in torch.chunk(t, chunks=2, dim=1):
print(x)
"""
tensor([[1, 2],
[5, 6]])
tensor([[3, 4],
[7, 8]])
"""
这里需要了解dim=0和等于1的区别
一个是 外维 一个是内维
dim范围是-2到1(在这里的范围)
dim范围和输入的维度有关
2.12 常用的操作入门
import torch
x=torch.arange(12)
print([x])
print(x.shape)
a=x.reshape(3,-1)
print(a)
y=x.reshape(3,4)
print(y)
print(y.shape)
print(y.numel())
print(torch.zeros(3,4))
print(torch.ones(3,4))
print(torch.randn(2,3,4))#随机梯度
print(torch.rand(9))#随机0,1
arange(12)表示0到11顺序的矩阵
print([x])是在print(x)整体的输出在加[]而不是仅仅在矩阵外面加一层括号
x.shape表示这个矩阵的大小,比如x就是一维矩阵,而y就是3*4的矩阵
x.reshape(3,4)表示把一维矩阵转换为二维矩阵,好用多用,但是记得两个矩阵的元素数量需要相同不然就报错了
x.reshape(3,-1)等同于x.reshape(3,4)也等同于x.reshape(-1,4),因为上面我们以及定义了x的元素个数总共有12个,所以给定矩阵的行数或者列数的一个,系统自动就转换了
后面的东西前面都见过了就不赘述了
2.13运算符
import torch
# x=torch.tensor([1,2,3,4])
# y=torch.tensor([5,6,7,8])
# print(x+y)
# print(x/y)
# print(x**y)
# print(x.exp())#指数
# print(torch.exp(y))
a=torch.arange(12,dtype=torch.float64).reshape(3,4)
b=torch.tensor([[4,3,2,1],[8,7,6,5],[4,3,2,1]])
print(a)
print(b)
print(a<b)
print(a==b)
# print(torch.cat((a,b),dim=0))#dim范围在-2到1
# print(torch.cat((a,b),dim=1))
# print(torch.cat((a,b),dim=-1))
# print(torch.cat((a,b),dim=-2))
print(a.sum())
这个运算结果就不展示了,大家自己动手探索一下
特别是cat的几种连接方式
2.14广播机制
广播机制就是两个形状不同的矩阵,通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状
看上面图片,我用的是第一个矩阵加第二个矩阵哈,所以你一一对应,第一行就是0+0,0+1,0+2
第二行1+0,1+1,1+2以此类推
2.15索引和切片
import torch
a=torch.arange(12,dtype=torch.float64).reshape(3,4)
# print(a)
# print(a[-1])
# print(a[1:3])
# a[0,1]=9
# print(a)
a[0:1,:1]=12
print(a)
a[0:1,:]=12
print(a)
a[0:1,-1:]=12
print(a)
注释部分的输出也在下面这个图片里
a[-1]就是最后一行
a[1:3]就是第二行和最后一行,记得是从1开始,但不包含3,如果把3改为2,那么就只输出第一行了,还有记得是从0开始计数
a[0,1]j就是把对应的位置数值进行修改
这里留一个问题,
a[0:1,-1:]=12
print(a)这行代码其实正常运行与下面图片输出不符,大家可以把a重置自己运行一遍
2.16节省内存
import torch
a=torch.arange(12,dtype=torch.float64).reshape(3,4)
b=torch.tensor([[4,3,2,1],[8,7,6,5],[4,3,2,1]])
print(id(a))
c=torch.zeros_like(a)
print(id(c))
c[:]=a+b #c=a+b是不行的
print(id(c))
# a+=b
# print(id(a))
# a+=b
# print(id(a))
# a+=b
# print(id(a))
# a+=b
# print(id(a))
# a+=b
# print(id(a))
# for i in range(100): #重复了100次依旧没有改变
# a += b
# print(id(a))
运行一些操作可能会导致为新结果分配内存。 例如,如果我们用
Y = X + Y
,我们将取消引用
Y
指向的张量,而是指向新分配的内存处的张量
我们可以使用切片表示法将操作的结果分配给先前分配的数组,例如
Y[:] = <expression>
。 为了说明这一点,我们首先创建一个新的矩阵
Z
,其形状与另一个
Y
相同, 使用
zeros_like
来分配一个全0的块。
如果在后续计算中没有重复使用
X
, 我们也可以使用
X[:] = X + Y
或
X += Y
来减少操作的内存开销。
2.17张量标量转换
张量转标量用.item()
标量转张量用.tensor()【比如说一个numpy数组c,可以
d=torch.tensor(c)来转为张量
2.2数据预处理
数据预处理一般用pandas库
pandas库啊,你用用就知道咋样了
一般啊,你先用os库创建个文件夹,存放人工数据集
在一顿操作进行命名,然后求值的时候你可能会遇到求平均值.mean()好用多用
最后处理缺失值(NaN)
摘抄于书中便于大家理解(主要我有点瞌睡了,不好意思):通过位置索引
iloc
,我们将
data
分成
inputs
和
outputs
, 其中前者为
data
的前两列,而后者为
data
的最后一列。 对于
inputs
中缺少的数值,我们用同一列的均值替换“NaN”项。
pandas库真是好库
- Series:一维数组,可以存储任何数据类型(整数、字符串、浮点数、Python 对象等),每个元素都有一个标签(索引)。
- DataFrame:二维的、表格型的数据结构,可以看作是由多个 Series 组成的字典(共享同一个索引)
这是他的核心结构,第一次见他还是爬虫处理数据时,好久了啊,旧码重写,就像旧事重提。
2.3线性代数
明天更新啊哈哈
———————————————————————————————————————————
早上好朋友们 今天是7.10号,初中朋友要同学聚会,可惜这次去不了了...
2.31标量
标量由只有一个元素的张量表示
x = torch.tensor(3.0)
y = torch.tensor(2.0)
2.32向量
向量可以被视为标量值组成的列表。 这些标量值被称为向量的元素(element)或分量(component)
x = torch.arange(4)
2.33张量
基于 标量 向量 矩阵 来看张量,上述三个分别为零维,一维,二维张量
引入矩阵的转置概念:假设有一个34的矩阵,转置之后就成了43的矩阵
这个就是转置的样式,假设B=A转置,那么A【ij】=B【ji】
两个矩阵的按元素乘法称为Hadamard积(Hadamard product)
假设A和B都是34的有序矩阵,那么AB的每一个结果就是A【ij】*B【ij】
tips:将张量乘以或加上一个标量不会改变张量的形状,其中张量的每个元素都将与标量相加或相乘
2.34降维
我们先理解axis
对矩阵内的元素我们常常使用求和操作,正常的.sum()会将矩阵内所有的元素进行求和
假设我们有一个形状为
(m, n, p)
的三维张量,其
axis
的范围可以解释如下:
axis = 0
:沿着第一个维度(大小为m的维度)进行操作。axis = 1
:沿着第二个维度(大小为n的维度)进行操作。axis = 2
:沿着第三个维度(大小为p的维度)进行操作。axis = -1
:与axis = 2
相同,也是沿着第三个维度(大小为p的维度)进行操作。axis = -2
:沿着第二个维度(大小为n的维度)进行操作,与axis = 1
相同
然后我们以实际例子来验证
由结果不难发现,axis=0时把(2,3,4)中的2压缩了,生成了一个(3,4)的矩阵
大家可以试试更多维度的张量,axis范围处于最大维度的正负数之间
2.35点积
这个就是x,y向量点积的表示方法
点积的结果:相同位置的按元素乘积的和
torch.dot(x,y)为代码形式
2.36矩阵向量积
学会了点积就会向量积了
a的行向量中每一个元素与相对应的b的元素相乘再加和得到向量积输出
比如说14=00+11+22+33
2.37矩阵相乘
分析一下42=00+13+26+39也就是a矩阵的第一行元素与b向量的第一列元素分别相乘再求和
也就是进行第一个矩阵行数m*第二个矩阵列数n次向量积运算
2.38范数
矩阵范数分为四类:列范数,行范数,l2范数和F范数
1..列范数,就是每一列绝对值求和中的最大值
比如说这个矩阵求每一列绝对值求和为【6,14,4】
那么列范数就是14
2.行范数,就是每一行绝对值求和中的最大值
依旧按上面矩阵为例每一行绝对值求和为【8,3,13】
- 2范数
矩阵a转置a的最大特征值下的平方根
先算a转置*a
入e-a=0转置a中入为最大特征值
求这个入是线代中求特征值的
4.F范数就是各个元素平方和再开根
2.4微积分
2.41导数和微分
这里提一个东西
如果出现:OMP: Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initialized.的报错
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"#在程序中发现了多个 OpenMP 运行时库的初始化。这可能会导致性能下降或产生错误结果,因为多个运行时库之间的交互可能会导致混乱。
#上述代码可以避免报错
以f(x)=3x*2-4x为例,内容都体现在代码注释里了
import torch
import numpy as np
from matplotlib_inline import backend_inline
from matplotlib import pyplot as plt
from d2l import torch as d2l
def f(x):
return 3 * x ** 2 - 4 * x
def numerical_lim(f, x, h):
return (f(x + h) - f(x)) / h
h = 0.1
for i in range(5):
print(f'h={h:.5f}, numerical limit={numerical_lim(f, 1, h):.5f}')
h *= 0.1#使h接近于0
def use_svg_display(): #@save
"""使用svg格式在Jupyter中显示绘图"""
backend_inline.set_matplotlib_formats('svg')
def set_figsize(figsize=(3.5, 2.5)):
"""设置matplotlib的图表大小"""
use_svg_display()
d2l.plt.rcParams['figure.figsize'] = figsize
def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):
"""设置matplotlib的轴
设置matplotlib Axes对象的属性。
参数:
axes (matplotlib.axes.Axes): 需要设置的Axes对象。
xlabel (str): X轴的标签。
ylabel (str): Y轴的标签。
xlim (tuple): X轴的范围,格式为(最小值, 最大值)。
ylim (tuple): Y轴的范围,格式为(最小值, 最大值)。
xscale (str): X轴的比例尺,如'linear', 'log'等。线性比例尺和对数比例尺
yscale (str): Y轴的比例尺,如'linear', 'log'等。
legend (list of tuples, optional): 图例项,格式为[(label1, line1), (label2, line2), ...]
其中label是字符串,line是Axes上的线对象。
"""
axes.set_xlabel(xlabel)
axes.set_ylabel(ylabel)
axes.set_xscale(xscale)
axes.set_yscale(yscale)
axes.set_xlim(xlim)
axes.set_ylim(ylim)
if legend:
axes.legend(legend)
axes.grid()#添加网格线
#@save
def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None,
ylim=None, xscale='linear', yscale='linear',#linear指的是线性刻度
fmts=('-', 'm--', 'g-.', 'r:'), figsize=(3.5, 2.5), axes=None):
# ax.plot(x, y1, fmts[0], label='Data 1') # 使用'-'线型
# ax.plot(x, y2, fmts[1], label='Data 2') # 使用'm--'线型,m代表品红色
# ax.plot(x, y3, fmts[2], label='Data 3') # 使用'g-.'线型,g代表绿色
# ax.plot(x, y4, fmts[3], label='Data 4') # 使用'r:'线型,r代表红色
"""绘制数据点"""
if legend is None:
legend = []
set_figsize(figsize)
axes = axes if axes else d2l.plt.gca()#获取当前坐标轴
# 如果X有一个轴,输出True
def has_one_axis(X):
return (hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list)
and not hasattr(X[0], "__len__"))
'''
hasattr(X, "ndim") and X.ndim == 1:
这部分首先检查X是否具有ndim属性。在NumPy数组中,ndim属性表示数组的维度数。
如果X具有ndim属性,并且X.ndim == 1,即X是一个一维数组,那么这部分条件为真。
isinstance(X, list) and not hasattr(X[0], "__len__"):
这部分首先检查X是否是一个列表(list)。
如果X是列表,接下来检查列表中的第一个元素(X[0])是否没有__len__方法。在Python中,许多容器类型(如列表、元组、字符串等)都有__len__方法,用于返回容器中元素的数量。如果一个对象没有__len__方法,那么它通常不是容器类型,也就是说,它不是另一个列表、元组、字符串等。
如果X是列表,并且其第一个元素不是容器类型(即没有__len__方法),那么这部分条件为真。'''
if has_one_axis(X):
X = [X]
if Y is None:
X, Y = [[]] * len(X), X
'''
X, Y = [[]] * len(X), X:这行代码做了两件事。
首先,它创建了一个与X长度相同的列表列表(二维列表),其中每个内部列表都是空的([[]] * len(X))。
然后,它将原始的X赋值给Y。'''
elif has_one_axis(Y):
Y = [Y]
if len(X) != len(Y):
X = X * len(Y)
axes.cla()#清除坐标轴内容
for x, y, fmt in zip(X, Y, fmts):
if len(x):
axes.plot(x, y, fmt)
else:
axes.plot(y, fmt)
set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
x = np.arange(0, 3, 0.1)
plot(x, [f(x), 2 * x - 3], 'x', 'f(x)', legend=['f(x)', 'Tangent line (x=1)'])
plt.show()
2.42偏导数和梯度
一、定义
- 偏导数(Partial Derivative): - 偏导数用于多变量函数,它描述了函数在一个特定变量方向上的变化率,而其他变量保持不变。- 例如,对于函数f(x,y),其关于x的偏导数表示为∂x∂f,关于y的偏导数表示为∂y∂f。
- 梯度(Gradient): - 梯度是一个矢量,它包含了一个多变量函数所有偏导数的信息。- 对于函数f(x,y),其梯度是一个二维向量,表示为∇f=(∂x∂f,∂y∂f)。对于更高维的函数f(x1,x2,…,xn),其梯度则是一个n维向量。 ---
二、关系
- 梯度是偏导数的向量形式: - 梯度将函数在各个方向上的偏导数组合成一个向量,这个向量在几何上表示了函数在该点处变化最快的方向。- 因此,可以说梯度是偏导数的集合,以向量的形式呈现。
- 梯度方向与变化率: - 梯度的方向是函数在该点处变化最快的方向(即最陡峭的方向),而梯度的模(长度)则表示了这种变化率的大小。- 换句话说,梯度不仅告诉我们函数在哪个方向上变化最快,还告诉我们这种变化的速度有多快
可以参考哔哩哔哩中的梯度动画进行理解
2.43自动微分
import torch
x = torch.arange(4.0)
x
x.requires_grad_(True) # 等价于x=torch.arange(4.0,requires_grad=True)
x.grad # 默认值是None
x.requires_grad_是在设置随后的操作之后能够计算x的梯度
x.grad是存储x的梯度
y = 2 * torch.dot(x, x)
y
然后
x
是一个长度为4的向量,计算
x
和
x
的点积,得到了我们赋值给
y
的标量输出。 接下来,通过调用反向传播函数来自动计算
y
关于
x
每个分量的梯度,并打印这些梯度。
y.backward()
x.grad
tips:在PyTorch中,每次调用
.backward()
后,PyTorch会保留计算的梯度,但如果你再次调用
.backward()
而不先清零梯度(使用
optimizer.zero_grad()
或
x.grad.zero_()
),PyTorch会累加梯度而不是覆盖它们。然而,这通常不会导致
x.grad
为
None
,除非在调用
.backward()
之前或之后显式地将
x.grad
设置为
None
。
另外x得为向量,标量可不行
2.44分离计算
z与y,x有关,y与x有关
z与两个变量有关,想求x的偏导,就得先用一个u存放y的数值视作常数。
x.grad.zero_()
y = x * x
u = y.detach()
z = u * x
z.sum().backward()
x.grad == u
.detach()就是创建一个与y一样的张量给u
计算图(Computational Graph)是一个用于表示和存储所有操作(如加法、乘法、激活函数等)及其依赖关系的结构。当你对张量(Tensor)进行操作时,PyTorch会自动构建一个计算图来跟踪这些操作。这个计算图对于自动微分(Automatic Differentiation)至关重要,因为它允许PyTorch在需要时反向遍历图来计算梯度。
就是啥吧,你z只想影响x,不想影响y与x的梯度,这个时候就需要u暂时顶替一下
x成绩不好,z是老师要找家长,x怕父母知道了影响家庭和睦,所以找来u来充当父母去跟老师谈话。
2.5概率
求我现在是否晚饭的概率
先掷一个骰子吧
fair_probs = torch.ones([6]) / 6
multinomial.Multinomial(1, fair_probs).sample()
.sample()是把样本都取出来
6个样本只有一个是1其他都是0
multinomial.Multinomial(10, fair_probs).sample()
这个的话就是掷10次骰子看每个反向的次数
如果是概率的话就除以掷的次数
2.51概率论知识
1.任意概率不为0
2.总概率为1
3.联合概率,也就是a和b同时发生
4.贝叶斯定理
想象你是一位侦探,正在调查一起案件。你有两个嫌疑人,我们称他们为A和B。在没有进一步证据的情况下,你根据初步调查认为A和B的作案嫌疑是相等的,即各占50%的嫌疑。然而,随着调查的深入,你发现了一些新的线索,这些线索可能与案件有关,也可能与嫌疑人有关。
贝叶斯定理的比喻:
- 先验概率:在没有新线索之前,你认为A和B的作案嫌疑各为50%。这里的50%就是先验概率,它是你根据已知信息(即初步调查)对事件发生的概率所做的估计。
- 新线索(证据):调查过程中,你发现了一个重要的指纹线索,经过比对发现该指纹与嫌疑人A的指纹相匹配。这个指纹线索就是新的证据,它会影响你对A和B作案嫌疑的判断。
- 似然率(调整因子):指纹线索与A相匹配这一事实,增加了A作案的可能性,同时降低了B作案的可能性。这个增加或减少的比例就是似然率或调整因子。在这个比喻中,似然率可能非常高,因为指纹是高度个人化的证据。
- 后验概率:结合先验概率和新线索的似然率,你可以计算出在发现指纹线索后A和B的作案嫌疑。这个新的嫌疑比例就是后验概率。由于指纹线索与A高度匹配,因此A的后验概率会远高于50%,而B的后验概率则会相应降低。
具体计算:
虽然在这个比喻中我们没有进行具体的数值计算,但贝叶斯定理的公式可以帮助我们进行这样的计算。如果设A作案为事件A,指纹线索为事件B,则贝叶斯定理可以表示为:
P(A|B) = P(B|A) * P(A) / P(B)
其中:
- P(A|B) 是后验概率,即在发现指纹线索后A作案的概率。
- P(B|A) 是似然率,即如果A作案,则发现指纹线索的概率。
- P(A) 是先验概率,即在没有新线索之前A作案的概率。
- P(B) 是指纹线索出现的总概率,它可以通过全概率公式计算得到。
期望和方差呢,都蛮不错的
版权归原作者 斩棘生. 所有, 如有侵权,请联系我们删除。