时间快,这块不是重点;要把动手编程变为重点。
机器学习是一门讨论各式各样的适用于不同问题的函数形式,以及如何使用数据来有效地获取函数参数具体值的学科。深度学习是指机器学习中的一类函数,它们的形式通常为多层神经网络。
自然法则,是从数据回归到自然定律,可以用线性模型完美表达的。(发现自然定律的过程,也可以认为是深度学习的过程。)
对于深度学习的认识,可以认为是数据分析的高度解读,将数据回归到规律上的过程。
赫布理论是感知机学习算法的原型,并成为随机梯度下降算法的基石:强化合意的行为、惩罚不合意的行为,最终获得优良的神经网络参数。
深度学习模型也可以看作是由许多简单函数复合而成的函数。(通过多函数来表达复杂的变换)
不是将单独调试的部分拼凑起来组成一个系统,而是将整个系统组建好之后一起训练。(自动优化的逐级过滤器)。
当数据多的时候,可以采用无参数模型(因为完全可以通过大数据来学习)
当数据少的时候,可以简化对现实的假设来得到实用的模型,采用含参数模型。
知道如何使用笔记本运行代码
import torch
import numpy as np
#创建随机初始化的Tensor
x1 = torch.rand(5, 3)
print("随机初始化的Tensor:\n{}\n".format(x1))
#创建long型全为0的Tensor
x2 = torch.zeros(5, 3, dtype=torch.long)
print("long型全为0的Tensor:\n{}\n".format(x2))
#直接根据数据来构建的Tensor
x3 = torch.tensor([5.5, 3.3])
x4 = torch.tensor(np.array([5.5, 3.3]))
print("直接根据数据来构建的Tensor:\n{}\n".format(x3))
print("直接根据数据来构建的Tensor(numpy形式):\n{}\n".format(x4))
#通过先有Tensor来构建的Tensor
x = x2.new_ones(5, 3, dtype=torch.float64)
print("通过先有Tensor来构建的Tensor:\n{}\n".format(x))
#指定新的数据类型;本来是ones,给变成random形式了
x= torch.randn_like(x, dtype=torch.float)
print("指定新的数据类型:\n{}\n".format(x))
y = torch.rand(5, 3)
print("y and x + y ")
print("y={}".format(y))
print("x + y ={}".format(x + y))
#通过先有Tensor来构建的Tensor
x = x2.new_ones(5, 3, dtype=torch.float64)
print("通过先有Tensor来构建的Tensor:\n{}\n".format(x))
#指定新的数据类型;本来是ones,给变成random形式了
x= torch.randn_like(x, dtype=torch.float)
print("指定新的数据类型:\n{}\n".format(x))
#输出Tensor形状
print(x.size())
print(x.shape)
#修改Tensor的值
x = x2.new_ones(5, 3, dtype=torch.float64)
print("通过先有Tensor来构建的Tensor:\n{}\n".format(x))
x[1][2] = 2
print("修改后的Tensor:\n{}\n".format(x))
#求导
x = torch.tensor([[1., -1.], [1., 1.]], requires_grad=True)
out = x.pow(4).sum()
out.backward()
x.grad
结果:
随机初始化的Tensor:
tensor([[0.2630, 0.5941, 0.1872],
[0.1692, 0.8500, 0.2259],
[0.3602, 0.0736, 0.3236],
[0.8318, 0.0060, 0.9432],
[0.8817, 0.4153, 0.0349]])
long型全为0的Tensor:
tensor([[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
直接根据数据来构建的Tensor:
tensor([5.5000, 3.3000])
直接根据数据来构建的Tensor(numpy形式):
tensor([5.5000, 3.3000], dtype=torch.float64)
通过先有Tensor来构建的Tensor:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
指定新的数据类型:
tensor([[-0.0822, 0.3507, -0.4749],
[-0.6580, 0.9162, 0.1962],
[-0.4497, 0.0201, -0.9021],
[ 0.7282, 1.2726, 0.2614],
[-1.5695, 1.0593, 0.1956]])
y and x + y
y=tensor([[0.6761, 0.5520, 0.0738],
[0.5443, 0.1203, 0.9336],
[0.7933, 0.6119, 0.7786],
[0.1704, 0.1668, 0.7125],
[0.4866, 0.0854, 0.0205]])
x + y =tensor([[ 0.5939, 0.9027, -0.4011],
[-0.1136, 1.0365, 1.1298],
[ 0.3436, 0.6321, -0.1235],
[ 0.8985, 1.4394, 0.9738],
[-1.0828, 1.1447, 0.2161]])
索引:
tensor([0.9178, 1.3507, 0.5251])
tensor([0.9178, 1.3507, 0.5251])
索引出来的结果与原数据共享内存,也就是如果修改一个,另一个也会被修改;
#通过先有Tensor来构建的Tensor
x = x2.new_ones(5, 3, dtype=torch.float64)
print("通过先有Tensor来构建的Tensor:\n{}\n".format(x))
#指定新的数据类型;本来是ones,给变成random形式了
x= torch.randn_like(x, dtype=torch.float)
print("指定新的数据类型:\n{}\n".format(x))
y = torch.rand(5, 3)
print("y and x + y ")
print("y={}".format(y))
print("x + y ={}".format(x + y))
#索引
y = x[0, :]
y +=1
print(y)
print(x[0, :])
结果:
通过先有Tensor来构建的Tensor:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
指定新的数据类型:
tensor([[-1.7176, 1.6462, 0.4919],
[-1.3136, 0.2945, -0.3432],
[ 0.5023, 0.7090, -0.4784],
[ 2.0734, -1.2543, -1.2498],
[-1.1179, 0.2308, -0.9198]])
torch.Size([5, 3])
torch.Size([5, 3])
通过先有Tensor来构建的Tensor:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
修改后的Tensor:
tensor([[1., 1., 1.],
[1., 1., 2.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
如果想要不共享内存,可以使用clone;
#不想共享内存的索引
x_clone = x.clone()
y = x_clone[0, :]
y +=1
print("不共享内存的索引:\n")
print(y)
print(x[0, :])
结果:
不共享内存的索引:
tensor([1.9178, 2.3507, 1.5251])
tensor([0.9178, 1.3507, 0.5251])
用view()来改变形状,但是view()只是改变了观察张量的角度,但是数据部分仍然是共享的(即改变一个角度的张量,其他的也会跟着改变;就像《龙珠》中的天津饭的分身术一样)
#改变形状
print("改变形状:\n")
y = x.view(15)
z = x.view(-1, 5)
print("x.size={}\ny.size={}\nz.size={}".format(x.size(), y.size(), z.size()))
结果:
改变形状:
x.size=torch.Size([5, 3])
y.size=torch.Size([15])
z.size=torch.Size([3, 5])
item()将Tensor转变为Python number
x = torch.randn(1)
print(x)
print(x.item())
函数 | 功能 |
---|---|
trace | 对角线元素之和(矩阵的迹) |
diag | 对角线元素 |
triu/tril | 矩阵的上三角/下三角,可指定偏移量 |
mm/bmm | 矩阵乘法,batch的矩阵乘法 |
addmm/addbmm/addmv/addr/badbmm… | 矩阵运算 |
t | 转置 |
dot/cross | 内积/外积 |
inverse | 求逆矩阵 |
svd | 奇异值分解 |
torch.range(start=1, end=6) 的结果是会包含end的,
而torch.arange(start=1, end=6)的结果并不包含end。
x = torch.arange(1,3).view(1, 2)
print("x={}".format(x))
y = torch.arange(1,4).view(3, 1)
print("y={}".format(y))
print("x + y ={}".format(x+y))
像y= x+y这样的运算会新开内存
x = torch.tensor([1, 2])
y = torch.tensor([3, 4])
id_before = id(y)
y = y + x
print(id(y) == id_before)#没加索引
可以通过索引的方式来进行替换操作,可以就可以指定结果到原来的y的内存里了
x = torch.tensor([1, 2])
y = torch.tensor([3, 4])
id_before = id(y)
y[:] = y + x
print(id(y) == id_before)#加索引的情况
还可以使用运算符全名函数中的out参数或者自加函数来达到以上结果
x = torch.tensor([1, 2])
y = torch.tensor([3, 4])
id_before = id(y)
y += x #torch.add(x, y, out=y)
print(id(y) == id_before)
使用numpy()将Tensor转换为NumPy数组(他们之间是共享相同的内存,改变一个另一个随着变化)
#使用numpy()
a = torch.ones(5)
b = a.numpy()
print(a, b)
a+=1
print(a, b)
b+=1
print(a, b)
使用from_numpy()将NumPy转换为Tensor(他们之间是共享相同的内存,改变一个另一个随着变化)
a = np.ones(5)
b = torch.from_numpy(a)
print(a, b)
a+=1
print(a, b)
b+=1
print(a, b)
使用torch.tensor()将NumPy数组转换为Tensor(该方法只是对数据进行拷贝,并不会再共享内存)
c = torch.tensor(a)
print(a, c)
a += 1
print(a, c)
将Tensor的属性.requires_grad设置为TRUE,就可以跟踪在其上的所有操作,之后可以通过链式法则来进行梯度传播;完成计算后,可以调用.backward()来完成所有梯度的计算;此Tensor的梯度将累积到.grad属性中。
调用.detach()将其从跟踪记录中剥离出来;
with torch.no_grad()将不想被追踪的代码包裹起来。
创建一个Tensor,并设置requires_grad=True(由于x是刚创建的值,所以grad_fn没有值)
x = torch.ones(2, 2, requires_grad=True)
print(x)
print(x.grad_fn)#none
添加运算
y = x + 2
print(y)
print(y.grad_fn)#
#复杂一点的操作
z = y * y * 3
out = z.mean()
print(z.grad_fn)#
out.backward()
print(x.grad)
通过.requires_grad_()来用in-place的方式改变requires_grad属性:
a = torch.randn(2, 2)
print(a)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
grad在反向传播中是累加的,所以一般在反向传播之前进行梯度清零
out2 = x.sum()
out2.backward()
print(x.grad)
out3 = x.sum()
x.grad.data.zero_()
out3.backward()
print(x.grad)
不允许张量对张量求导,只允许标量对张量求导,求导结果是与自变量同形的张量(例如上面在求导时,都是mean,或sum等函数之后得到的标量)
x = torch.tensor([1.0, 2.0, 3.0, 4.0], requires_grad=True)
y = 2 * x
z = y.view(2, 2)
print(z)
v = torch.tensor([[1.0, 0.1], [0.01, 0.001]], dtype=torch.float)
z.backward(v)
print(x.grad)
中断梯度跟踪
x = torch.tensor(1.0, requires_grad=True)
y1 = x ** 2
with torch.no_grad():
y2 = x ** 3
y3 = y1 + y2
print(x.requires_grad)#TRUE
print(y1.requires_grad)#TRUE
print(y2.requires_grad)#FALSE
print(y3.requires_grad)#TRUE
y3.backward()
print(x.grad)#只有y1有关的梯度才会回传
想要修改Tensor的值,但又不想被autograd记录(不影响反向传播),可以对tensor.data进行操作
x = torch.tensor(1.0, requires_grad=True)
print(x.data)
print(x.data.requires_grad)#False
y = 2 * x
x.data *= 100
y.backward()
print(x)#对x.data更改的值,也会保存在x中;tensor(100., requires_grad=True)
print(x.data)#tensor(100.)