均方差Mean Squared Error(MSE)
MSE基本形式:
l
o
s
s
=
∑
[
y
−
(
x
w
+
b
)
]
2
loss = \sum[y-(xw+b)]^2
loss=∑[y−(xw+b)]2
这里的模型使用非常简单的线性感知机,如果使用其他的网络,则修改为
l
o
s
s
=
∑
[
y
−
f
(
x
;
w
,
b
)
]
2
loss = \sum[y-f(x;w,b)]^2
loss=∑[y−f(x;w,b)]2
需要注意的是,这里的均方差MSE和L2-norm是有区别的:
l
2
−
n
o
r
m
=
∑
(
y
1
−
y
2
)
2
l2-norm=\sqrt{\sum{(y_1-y_2)^2}}
l2−norm=∑(y1−y2)2
PyTorch在MSE中的使用:torch.norm(y-y',2).pow(2)
MSE梯度
l
o
s
s
=
∑
[
y
−
f
θ
(
x
)
]
2
∇
l
o
s
s
∇
θ
=
2
∑
[
y
−
f
θ
(
x
)
]
∗
∇
f
θ
(
x
)
∇
θ
loss = \sum[y-f_\theta(x)]^2 \\ \frac{\nabla loss}{\nabla \theta} = 2\sum[y-f_\theta(x)]*\frac{\nabla f_\theta(x)}{\nabla \theta}
loss=∑[y−fθ(x)]2∇θ∇loss=2∑[y−fθ(x)]∗∇θ∇fθ(x)
因此,如果使用简单的线性回归,那么
f
(
x
)
=
w
x
+
b
f(x)=wx+b
f(x)=wx+b,那么对于
∇
f
θ
(
x
)
∇
θ
\frac{\nabla f_\theta(x)}{\nabla \theta}
∇θ∇fθ(x)则为(x,1)
使用torch.autograd.grad(loss,[w1,w2…])求导
返回结果是list的方式:[w1 grad, w2 grad, w3 grad .....]
这里使用最简单的线性模型
y
=
w
x
y=wx
y=wx
x = torch.ones(1)
w = torch.full([1],2)
w = w.type_as(torch.FloatTensor()) # 将w由LongTensor转为FloatTensor,否则无法设置梯度
w.requires_grad_() # tensor([2.], requires_grad=True) 设置w的梯度
mse = F.mse_loss(x*w ,torch.ones(1)) # tensor(1.) 这里假设label是1
torch.autograd.grad(mse,[w]) # 第一个参数是y(loss),第二个参数是参数
# (tensor([2.]),)
l
o
s
s
=
(
1
−
1
∗
2
)
2
∂
l
o
s
s
∂
w
=
2
∗
(
1
−
2
)
∗
(
−
1
)
∂
l
o
s
s
∂
w
=
2
loss = (1-1*2)^2 \\ \frac{\partial loss}{\partial w} = 2 * (1-2)*(-1)\frac{\partial loss}{\partial w} = 2
loss=(1−1∗2)2∂w∂loss=2∗(1−2)∗(−1)∂w∂loss=2
设置w的梯度还可以通过在初始化时进行设置,w = torch.tensor([1],requires_grad=True)
使用loss.backward()求导
不会额外返回结果,而是直接附加在每个成员变量上,结果是:w1.grad
,w2.grad
…
除了使用torch.autograd.grad(mse,[w])
方式求导外,还可以使用:
mse = F.mse_loss(x*w, torch.ones(1))
mse.backward() # 向后传播
w.grad
# tensor([2.])
mse.backward()
表示向后传播,PyTorch会自动记录下图的路径,因此在最后的loss节点上调用backward时,会完成这条路径上所有的需要梯度grad的计算,这个计算后的grad不会直接返回,而是会自动把grad信息附加在每个tensor的成员变量.grad
上,因为这个只有一个w
参数,因此只有一个w.grad
,
Cross Entropy Loss
常见的分类中的损失函数,既可以用于二分类,也可以用多分类,一般跟softmax激活函数搭配一起使用。
softmax激活函数
对于一个输出y,如果需要转为概率值,希望概率最大的值作为预测的label,如上图,2.0最大,其对应的索引是0,因此0就是一个label。但是我们的概率是属于一个区间,如果要把这个值转为概率值,需要人为进行压缩,可以使用sigmoid函数来完成,但是对于多分类来说,一个物体到底属于哪个类,有概率的大小之分,而这些概率之和为1。因此使用sigmoid并不是十分准确。
对于softmax的属性是:每一个值的大小范围是(0,1),所有概率之和为1。
S
(
y
i
)
=
e
y
i
∑
j
e
y
j
S(y_i) = \frac{e^{y_i}}{\sum_je^{y_j}}
S(yi)=∑jeyjeyi
对于上面的例子:
e
2
e
2
+
e
1
+
e
0.1
+
e
1
e
2
+
e
1
+
e
0.1
+
e
0.1
e
2
+
e
1
+
e
0.1
=
1
\frac{e^2}{e^2+e^1+e0.1}+\frac{e^1}{e^2+e^1+e0.1}+\frac{e^{0.1}}{e^2+e^1+e0.1} = 1
e2+e1+e0.1e2+e2+e1+e0.1e1+e2+e1+e0.1e0.1=1
之前的标签2和1的差距只有2倍,而经过softmax操作之后,0.7和0.2的差距却放大了。因此softmax会将原来的差距拉大。
softmax导数
p
i
=
e
a
i
∑
k
=
1
N
e
a
k
∂
p
i
∂
a
j
=
{
p
i
(
1
−
p
j
)
if i = j
−
p
j
⋅
p
i
,
if i
≠
j
p_i = \frac{e^{a_i}}{\sum_{k=1}^Ne^{a_k}} \\ \frac{\partial p_i}{\partial a_j} = \begin{cases} p_i(1-p_j) & \text {if i = j} \\ -p_j·p_i, & \text{if i}\ne j \end{cases}
pi=∑k=1Neakeai∂aj∂pi={pi(1−pj)−pj⋅pi,if i = jif i=j
可以根据公式看出当i=j时,梯度是大于0的,其他情况下是小于0的。
计算梯度
a = torch.rand(3)
a.requires_grad_() # tensor([0.2306, 0.6693, 0.6334], requires_grad=True)
p = F.softmax(a, dim=0)
# tensor([0.2471, 0.3832, 0.3697], grad_fn=<SoftmaxBackward>)
torch.autograd.grad(p[0],[a], retain_graph=True)
# (tensor([ 0.1860, -0.0947, -0.0914]),)
torch.autograd.grad(p[1],[a], retain_graph=True)
# (tensor([-0.0947, 0.2364, -0.1417]),)
torch.autograd.grad(p[2],[a], retain_graph=True)
# (tensor([-0.0914, -0.1417, 0.2330]),)
∂
p
0
∂
a
i
=
[
0.1860
,
−
0.0947
,
−
0.0914
]
\frac{\partial p_0}{\partial a_i}=[ 0.1860, -0.0947, -0.0914]
∂ai∂p0=[0.1860,−0.0947,−0.0914],其中
∂
p
0
∂
a
0
=
[
0.1860
]
,
∂
p
0
∂
a
1
=
[
−
0.0947
]
,
∂
p
0
∂
a
2
=
[
−
0.0914
]
\frac{\partial p_0}{\partial a_0}=[ 0.1860],\frac{\partial p_0}{\partial a_1}=[ -0.0947],\frac{\partial p_0}{\partial a_2}=[ -0.0914]
∂a0∂p0=[0.1860],∂a1∂p0=[−0.0947],∂a2∂p0=[−0.0914]可以看出当j=i
时,梯度信息是正的。