修改神经网络以对单个示例进行分类
Modify neural net to classify single example
这是我对深度学习课程中 Andrew NG 的神经网络之一的自定义扩展,我正在尝试为二进制分类生成 0 或 1 而不是
对多个示例进行分类。
输入和输出都是一个热编码。
在没有太多训练的情况下,我获得了 'train accuracy: 67.51658067499625 %'
的准确度
如何对单个训练样本进行分类而不是对所有训练样本进行分类?
我认为我的实现中存在一个错误,因为该网络的一个问题是训练示例 (train_set_x) 和输出值 (train_set_y) 都需要具有相同的维度或与接收到矩阵的维数。
例如使用:
train_set_x = np.array([
[1,1,1,1],[0,1,1,1],[0,0,1,1]
])
train_set_y = np.array([
[1,1,1],[1,1,0],[1,1,1]
])
returns 错误:
ValueError Traceback (most recent call last)
<ipython-input-11-0d356e8d66f3> in <module>()
27 print(A)
28
---> 29 np.multiply(train_set_y,A)
30
31 def initialize_with_zeros(numberOfTrainingExamples):
ValueError:操作数无法与形状 (3,3) (1,4) 一起广播
网络代码:
import numpy as np
import matplotlib.pyplot as plt
import h5py
import scipy
from scipy import ndimage
import pandas as pd
%matplotlib inline
train_set_x = np.array([
[1,1,1,1],[0,1,1,1],[0,0,1,1]
])
train_set_y = np.array([
[1,1,1,0],[1,1,0,0],[1,1,1,1]
])
numberOfFeatures = 4
numberOfTrainingExamples = 3
def sigmoid(z):
s = 1 / (1 + np.exp(-z))
return s
w = np.zeros((numberOfTrainingExamples , 1))
b = 0
A = sigmoid(np.dot(w.T , train_set_x))
print(A)
np.multiply(train_set_y,A)
def initialize_with_zeros(numberOfTrainingExamples):
w = np.zeros((numberOfTrainingExamples , 1))
b = 0
return w, b
def propagate(w, b, X, Y):
m = X.shape[1]
A = sigmoid(np.dot(w.T , X) + b)
cost = -(1/m)*np.sum(np.multiply(Y,np.log(A)) + np.multiply((1-Y),np.log(1-A)), axis=1)
dw = ( 1 / m ) * np.dot( X, ( A - Y ).T ) # consumes ( A - Y )
db = ( 1 / m ) * np.sum( A - Y ) # consumes ( A - Y ) again
# cost = np.squeeze(cost)
grads = {"dw": dw,
"db": db}
return grads, cost
def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = True):
costs = []
for i in range(num_iterations):
grads, cost = propagate(w, b, X, Y)
dw = grads["dw"]
db = grads["db"]
w = w - (learning_rate * dw)
b = b - (learning_rate * db)
if i % 100 == 0:
costs.append(cost)
if print_cost and i % 10000 == 0:
print(cost)
params = {"w": w,
"b": b}
grads = {"dw": dw,
"db": db}
return params, grads, costs
def model(X_train, Y_train, num_iterations, learning_rate = 0.5, print_cost = False):
w, b = initialize_with_zeros(numberOfTrainingExamples)
parameters, grads, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost = True)
w = parameters["w"]
b = parameters["b"]
Y_prediction_train = sigmoid(np.dot(w.T , X_train) + b)
print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
model(train_set_x, train_set_y, num_iterations = 20000, learning_rate = 0.0001, print_cost = True)
更新:此实现中存在错误,因为训练示例对 (train_set_x , train_set_y)
必须包含相同的维度。能否指出线性代数应该如何修改的方向?
更新 2:
我修改了@Paul Panzer 的答案,使学习率为 0.001 和 train_set_x,train_set_y 对是唯一的:
train_set_x = np.array([
[1,1,1,1,1],[0,1,1,1,1],[0,0,1,1,0],[0,0,1,0,1]
])
train_set_y = np.array([
[1,0,0],[0,0,1],[0,1,0],[1,0,1]
])
grads = model(train_set_x, train_set_y, num_iterations = 20000, learning_rate = 0.001, print_cost = True)
# To classify single training example :
print(sigmoid(dw @ [0,0,1,1,0] + db))
此更新产生以下输出:
-2.09657359028
-3.94918577439
[[ 0.74043089 0.32851512 0.14776077 0.77970162]
[ 0.04810012 0.08033521 0.72846174 0.1063849 ]
[ 0.25956911 0.67148488 0.22029838 0.85223923]]
[[1 0 0 1]
[0 0 1 0]
[0 1 0 1]]
train accuracy: 79.84462279013312 %
[[ 0.51309252 0.48853845 0.50945862]
[ 0.5110232 0.48646923 0.50738869]
[ 0.51354109 0.48898712 0.50990734]]
print(sigmoid(dw @ [0,0,1,1,0] + db))
是否应该生成一个向量,该向量一旦四舍五入就与 train_set_y
对应的值相匹配:[0,1,0]
?
修改以生成向量(将 [0,0,1,1,0]
添加到 numpy 数组并进行转置):
print(sigmoid(dw @ np.array([[0,0,1,1,0]]).T + db))
returns :
array([[ 0.51309252],
[ 0.48646923],
[ 0.50990734]])
同样,当预期 [0,1,0]
时,将这些值四舍五入为最接近的整数会生成向量 [1,0,1]
。
这些是为单个训练示例生成预测的错误操作?
你的困难来自不匹配的维度,所以让我们来解决这个问题并尝试弄清楚它们。
您的网络有许多输入和特征,我们称它们为 N_in
(在您的代码中为 numberOfFeatures
)。它有许多输出对应于不同的 classes 让我们称它们为 N_out
。输入和输出由权重 w
.
连接
问题来了。连接是全连接的,因此我们需要为 N_out x N_in
对输出和输入中的每一对分配一个权重。因此,在您的代码中,w
的形状必须更改为 (N_out, N_in)
。您可能还希望每个输出都有一个偏移量 b
,因此 b 应该是大小为 (N_out,)
或 (N_out, 1)
的向量,因此它可以很好地处理 2d 项。
我已在下面修改后的代码中修复了该问题,并尝试使其非常明确。我还加入了一个模拟数据创建者。
Re the one-hot encoded categorical output,我不是神经网络方面的专家,但我认为,大多数人都理解它,因此 classes 是互斥的,所以你的模拟输出中的每个样本应该有一个一,其余为零。
旁注:
有一次,一个竞争性答案建议您去掉成本函数中的 1-...
项。虽然这对我来说是一个有趣的想法,但我的直觉 (Edit 现在确认使用无梯度最小化器;在下面的代码中使用 activation="hybrid" 。Solver 将简单地最大化 all 输出在至少一个训练示例中处于活动状态。)它不会像那样工作,因为成本将无法惩罚误报(详见下文)。要使其正常工作,您必须添加某种正则化。一种似乎有效的方法是使用 softmax
而不是 sigmoid
。 softmax
是单热的,sigmoid
是二进制的。它确保输出为 "fuzzy one-hot".
因此我的建议是:
- 如果你想坚持使用
sigmoid
并且不明确强制执行 one-hot 预测。保留 1-...
项。
- 如果你想使用更短的成本函数。执行单热预测。例如,使用
softmax
而不是 sigmoid
。
我在模型之间切换的代码中添加了一个 activation="sigmoid"|"softmax"|"hybrid"
参数。我还提供了 scipy 通用最小化器,当成本梯度不在手头时,这可能会有用。
回顾成本函数的工作原理:
成本是所有class项和术语
的所有训练样本的总和
-y log (y') - (1-y) log (1-y')
其中 y 是预期响应,即 "y" 训练样本为输入("x" 训练样本)给出的响应。 y' 是预测,即网络及其当前权重和偏差生成的响应。现在,由于预期响应为 0 或 1,因此可以编写单个类别和单个训练样本的成本
-log (y') if y = 1
-log(1-y') if y = 0
因为在第一种情况下 (1-y) 为零,所以第二项消失,在第二种情况下 y 为零,所以第一项消失。
如果
,现在可以说服自己成本很高
- 预期响应 y 为 1,网络预测 y' 接近于零
- 预期响应 y 为 0,网络预测 y' 接近 1
换句话说,成本在惩罚错误预测方面起到了作用。现在,如果我们删除第二项 (1-y) log (1-y')
,该机制的一半就消失了。如果预期响应为 1,则低预测仍会产生成本,但如果预期响应为 0,则成本将为零,无论预测如何,特别是高预测(或 假阳性) 会逍遥法外。
现在,因为总成本是所有训练样本的总和,所以存在三种可能性。
所有训练样本都规定 class 为零:
那么成本将完全独立于此 class 的预测,并且无法进行任何学习
一些训练样本将 class 设为零,一些设为一:
然后因为 "false negatives" 或 "misses" 仍然受到惩罚但误报不是网络将找到最小化成本的最简单方法,即不加区别地增加所有样本的 class 的预测
所有训练样本规定 class 是一个:
与第二种情况基本相同,只是这里没有问题,因为这是正确的行为
最后,为什么我们使用 softmax
而不是 sigmoid
会起作用?误报仍然是不可见的。现在很容易看出 softmax 的所有 classes 的总和是 1。因此,如果至少减少另一个 class 以进行补偿,我只能增加一个 class 的预测。特别是,没有假阴性就没有假阳性,而假阴性成本将检测到。
关于如何获得二进制预测:
对于二进制预期响应,四舍五入确实是合适的程序。对于 one-hot,我宁愿找到最大值,将其设置为 1,将所有其他值设置为零。我添加了一个方便的函数,predict
,实现它。
import numpy as np
from scipy import optimize as opt
from collections import namedtuple
# First, a few structures to keep ourselves organized
Problem_Size = namedtuple('Problem_Size', 'Out In Samples')
Data = namedtuple('Data', 'Out In')
Network = namedtuple('Network', 'w b activation cost gradient most_likely')
def get_dims(Out, In, transpose=False):
"""extract dimensions and ensure everything is 2d
return Data, Dims"""
# gracefully acccept lists etc.
Out, In = np.asanyarray(Out), np.asanyarray(In)
if transpose:
Out, In = Out.T, In.T
# if it's a single sample make sure it's n x 1
Out = Out[:, None] if len(Out.shape) == 1 else Out
In = In[:, None] if len(In.shape) == 1 else In
Dims = Problem_Size(Out.shape[0], *In.shape)
if Dims.Samples != Out.shape[1]:
raise ValueError("number of samples must be the same for Out and In")
return Data(Out, In), Dims
def sigmoid(z):
s = 1 / (1 + np.exp(-z))
return s
def sig_cost(Net, data):
A = process(data.In, Net)
logA = np.log(A)
return -(data.Out * logA + (1-data.Out) * (1-logA)).sum(axis=0).mean()
def sig_grad (Net, Dims, data):
A = process(data.In, Net)
return dict(dw = (A - data.Out) @ data.In.T / Dims.Samples,
db = (A - data.Out).mean(axis=1, keepdims=True))
def sig_ml(z):
return np.round(z).astype(int)
def sof_ml(z):
hot = np.argmax(z, axis=0)
z = np.zeros(z.shape, dtype=int)
z[hot, np.arange(len(hot))] = 1
return z
def softmax(z):
z = z - z.max(axis=0, keepdims=True)
z = np.exp(z)
return z / z.sum(axis=0, keepdims=True)
def sof_cost(Net, data):
A = process(data.In, Net)
logA = np.log(A)
return -(data.Out * logA).sum(axis=0).mean()
sof_grad = sig_grad
def get_net(Dims, activation='softmax'):
activation, cost, gradient, ml = {
'sigmoid': (sigmoid, sig_cost, sig_grad, sig_ml),
'softmax': (softmax, sof_cost, sof_grad, sof_ml),
'hybrid': (sigmoid, sof_cost, None, sig_ml)}[activation]
return Network(w=np.zeros((Dims.Out, Dims.In)),
b=np.zeros((Dims.Out, 1)),
activation=activation, cost=cost, gradient=gradient,
most_likely=ml)
def process(In, Net):
return Net.activation(Net.w @ In + Net.b)
def propagate(data, Dims, Net):
return Net.gradient(Net, Dims, data), Net.cost(Net, data)
def optimize_no_grad(Net, Dims, data):
def f(x):
Net.w[...] = x[:Net.w.size].reshape(Net.w.shape)
Net.b[...] = x[Net.w.size:].reshape(Net.b.shape)
return Net.cost(Net, data)
x = np.r_[Net.w.ravel(), Net.b.ravel()]
res = opt.minimize(f, x, options=dict(maxiter=10000)).x
Net.w[...] = res[:Net.w.size].reshape(Net.w.shape)
Net.b[...] = res[Net.w.size:].reshape(Net.b.shape)
def optimize(Net, Dims, data, num_iterations, learning_rate, print_cost = True):
w, b = Net.w, Net.b
costs = []
for i in range(num_iterations):
grads, cost = propagate(data, Dims, Net)
dw = grads["dw"]
db = grads["db"]
w -= learning_rate * dw
b -= learning_rate * db
if i % 100 == 0:
costs.append(cost)
if print_cost and i % 10000 == 0:
print(cost)
return grads, costs
def model(X_train, Y_train, num_iterations, learning_rate = 0.5, print_cost = False, activation='sigmoid'):
data, Dims = get_dims(Y_train, X_train, transpose=True)
Net = get_net(Dims, activation)
if Net.gradient is None:
optimize_no_grad(Net, Dims, data)
else:
grads, costs = optimize(Net, Dims, data, num_iterations, learning_rate, print_cost = True)
Y_prediction_train = process(data.In, Net)
print(Y_prediction_train)
print(data.Out)
print(Y_prediction_train.sum(axis=0))
print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - data.Out)) * 100))
return Net
def predict(In, Net, probability=False):
In = np.asanyarray(In)
is1d = In.ndim == 1
if is1d:
In = In.reshape(-1, 1)
Out = process(In, Net)
if not probability:
Out = Net.most_likely(Out)
if is1d:
Out = Out.reshape(-1)
return Out
def create_data(Dims):
Out = np.zeros((Dims.Out, Dims.Samples), dtype=int)
Out[np.random.randint(0, Dims.Out, (Dims.Samples,)), np.arange(Dims.Samples)] = 1
In = np.random.randint(0, 2, (Dims.In, Dims.Samples))
return Data(Out, In)
train_set_x = np.array([
[1,1,1,1,1],[0,1,1,1,1],[0,0,1,1,0],[0,0,1,0,1]
])
train_set_y = np.array([
[1,0,0],[1,0,0],[0,0,1],[0,0,1]
])
Net1 = model(train_set_x, train_set_y, num_iterations = 20000, learning_rate = 0.001, print_cost = True, activation='sigmoid')
Net2 = model(train_set_x, train_set_y, num_iterations = 20000, learning_rate = 0.001, print_cost = True, activation='softmax')
Net3 = model(train_set_x, train_set_y, num_iterations = 20000, learning_rate = 0.001, print_cost = True, activation='hybrid')
Dims = Problem_Size(8, 100, 50)
data = create_data(Dims)
model(data.In.T, data.Out.T, num_iterations = 40000, learning_rate = 0.001, print_cost = True, activation='softmax')
model(data.In.T, data.Out.T, num_iterations = 40000, learning_rate = 0.001, print_cost = True, activation='sigmoid')
关于如何修复 bug 以及如何将实现扩展到 classify 在更多 classes 之间的想法都可以通过一些维度分析来解决。
我假设您通过 class 验证多个示例意味着多个 classes 而不是多个样本,因为我们需要多个样本来训练甚至 2 classes。
其中 N
= 样本数,D
= 特征数,K
= 类别数(K=2
是一种特殊情况,可以减少这下降到一个维度,即 K=1
和 y=0
表示一个 class 和 y=1
另一个)。数据应具有以下维度:
X: N * D #input
y: N * K #output
W: D * K #weights, also dW has same dimensions
b: 1 * K #bias, also db has same dimensions
#A should have same dimensions as y
维度的顺序可以调换,只要点积正确。
首先处理您的错误:您正在将 W
初始化为 N * K
而不是 D * K
即。在二进制情况下:
w = np.zeros((numberOfTrainingExamples , 1))
#instead of
w = np.zeros((numberOfFeatures , 1))
这意味着您唯一一次初始化 W
以更正尺寸是在 y
和 X
(巧合)具有相同尺寸时。
这也会弄乱你的点积:
np.dot(X, w) # or np.dot(w.T,X.T) if you define y as [K * N] dimensions
#instead of
np.dot(w.T , X)
和
np.dot( X.T, ( A - Y ) ) #np.dot( X.T, ( A - Y ).T ) if y:[K * N]
#instead of
np.dot( X, ( A - Y ).T )
还要确保成本函数 returns 是一个数字(即不是数组)。
接下来K>2
你需要做一些改变。 b
不再是一个数字,而是一个向量(一维数组)。 y
和 W
从一维数组变为二维数组。为避免混淆和难以发现的错误,最好将 K
、N
和 D
设置为不同的值
这是我对深度学习课程中 Andrew NG 的神经网络之一的自定义扩展,我正在尝试为二进制分类生成 0 或 1 而不是 对多个示例进行分类。
输入和输出都是一个热编码。
在没有太多训练的情况下,我获得了 'train accuracy: 67.51658067499625 %'
如何对单个训练样本进行分类而不是对所有训练样本进行分类?
我认为我的实现中存在一个错误,因为该网络的一个问题是训练示例 (train_set_x) 和输出值 (train_set_y) 都需要具有相同的维度或与接收到矩阵的维数。 例如使用:
train_set_x = np.array([
[1,1,1,1],[0,1,1,1],[0,0,1,1]
])
train_set_y = np.array([
[1,1,1],[1,1,0],[1,1,1]
])
returns 错误:
ValueError Traceback (most recent call last)
<ipython-input-11-0d356e8d66f3> in <module>()
27 print(A)
28
---> 29 np.multiply(train_set_y,A)
30
31 def initialize_with_zeros(numberOfTrainingExamples):
ValueError:操作数无法与形状 (3,3) (1,4) 一起广播
网络代码:
import numpy as np
import matplotlib.pyplot as plt
import h5py
import scipy
from scipy import ndimage
import pandas as pd
%matplotlib inline
train_set_x = np.array([
[1,1,1,1],[0,1,1,1],[0,0,1,1]
])
train_set_y = np.array([
[1,1,1,0],[1,1,0,0],[1,1,1,1]
])
numberOfFeatures = 4
numberOfTrainingExamples = 3
def sigmoid(z):
s = 1 / (1 + np.exp(-z))
return s
w = np.zeros((numberOfTrainingExamples , 1))
b = 0
A = sigmoid(np.dot(w.T , train_set_x))
print(A)
np.multiply(train_set_y,A)
def initialize_with_zeros(numberOfTrainingExamples):
w = np.zeros((numberOfTrainingExamples , 1))
b = 0
return w, b
def propagate(w, b, X, Y):
m = X.shape[1]
A = sigmoid(np.dot(w.T , X) + b)
cost = -(1/m)*np.sum(np.multiply(Y,np.log(A)) + np.multiply((1-Y),np.log(1-A)), axis=1)
dw = ( 1 / m ) * np.dot( X, ( A - Y ).T ) # consumes ( A - Y )
db = ( 1 / m ) * np.sum( A - Y ) # consumes ( A - Y ) again
# cost = np.squeeze(cost)
grads = {"dw": dw,
"db": db}
return grads, cost
def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = True):
costs = []
for i in range(num_iterations):
grads, cost = propagate(w, b, X, Y)
dw = grads["dw"]
db = grads["db"]
w = w - (learning_rate * dw)
b = b - (learning_rate * db)
if i % 100 == 0:
costs.append(cost)
if print_cost and i % 10000 == 0:
print(cost)
params = {"w": w,
"b": b}
grads = {"dw": dw,
"db": db}
return params, grads, costs
def model(X_train, Y_train, num_iterations, learning_rate = 0.5, print_cost = False):
w, b = initialize_with_zeros(numberOfTrainingExamples)
parameters, grads, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost = True)
w = parameters["w"]
b = parameters["b"]
Y_prediction_train = sigmoid(np.dot(w.T , X_train) + b)
print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
model(train_set_x, train_set_y, num_iterations = 20000, learning_rate = 0.0001, print_cost = True)
更新:此实现中存在错误,因为训练示例对 (train_set_x , train_set_y)
必须包含相同的维度。能否指出线性代数应该如何修改的方向?
更新 2:
我修改了@Paul Panzer 的答案,使学习率为 0.001 和 train_set_x,train_set_y 对是唯一的:
train_set_x = np.array([
[1,1,1,1,1],[0,1,1,1,1],[0,0,1,1,0],[0,0,1,0,1]
])
train_set_y = np.array([
[1,0,0],[0,0,1],[0,1,0],[1,0,1]
])
grads = model(train_set_x, train_set_y, num_iterations = 20000, learning_rate = 0.001, print_cost = True)
# To classify single training example :
print(sigmoid(dw @ [0,0,1,1,0] + db))
此更新产生以下输出:
-2.09657359028
-3.94918577439
[[ 0.74043089 0.32851512 0.14776077 0.77970162]
[ 0.04810012 0.08033521 0.72846174 0.1063849 ]
[ 0.25956911 0.67148488 0.22029838 0.85223923]]
[[1 0 0 1]
[0 0 1 0]
[0 1 0 1]]
train accuracy: 79.84462279013312 %
[[ 0.51309252 0.48853845 0.50945862]
[ 0.5110232 0.48646923 0.50738869]
[ 0.51354109 0.48898712 0.50990734]]
print(sigmoid(dw @ [0,0,1,1,0] + db))
是否应该生成一个向量,该向量一旦四舍五入就与 train_set_y
对应的值相匹配:[0,1,0]
?
修改以生成向量(将 [0,0,1,1,0]
添加到 numpy 数组并进行转置):
print(sigmoid(dw @ np.array([[0,0,1,1,0]]).T + db))
returns :
array([[ 0.51309252],
[ 0.48646923],
[ 0.50990734]])
同样,当预期 [0,1,0]
时,将这些值四舍五入为最接近的整数会生成向量 [1,0,1]
。
这些是为单个训练示例生成预测的错误操作?
你的困难来自不匹配的维度,所以让我们来解决这个问题并尝试弄清楚它们。
您的网络有许多输入和特征,我们称它们为 N_in
(在您的代码中为 numberOfFeatures
)。它有许多输出对应于不同的 classes 让我们称它们为 N_out
。输入和输出由权重 w
.
问题来了。连接是全连接的,因此我们需要为 N_out x N_in
对输出和输入中的每一对分配一个权重。因此,在您的代码中,w
的形状必须更改为 (N_out, N_in)
。您可能还希望每个输出都有一个偏移量 b
,因此 b 应该是大小为 (N_out,)
或 (N_out, 1)
的向量,因此它可以很好地处理 2d 项。
我已在下面修改后的代码中修复了该问题,并尝试使其非常明确。我还加入了一个模拟数据创建者。
Re the one-hot encoded categorical output,我不是神经网络方面的专家,但我认为,大多数人都理解它,因此 classes 是互斥的,所以你的模拟输出中的每个样本应该有一个一,其余为零。
旁注:
有一次,一个竞争性答案建议您去掉成本函数中的 1-...
项。虽然这对我来说是一个有趣的想法,但我的直觉 (Edit 现在确认使用无梯度最小化器;在下面的代码中使用 activation="hybrid" 。Solver 将简单地最大化 all 输出在至少一个训练示例中处于活动状态。)它不会像那样工作,因为成本将无法惩罚误报(详见下文)。要使其正常工作,您必须添加某种正则化。一种似乎有效的方法是使用 softmax
而不是 sigmoid
。 softmax
是单热的,sigmoid
是二进制的。它确保输出为 "fuzzy one-hot".
因此我的建议是:
- 如果你想坚持使用
sigmoid
并且不明确强制执行 one-hot 预测。保留1-...
项。 - 如果你想使用更短的成本函数。执行单热预测。例如,使用
softmax
而不是sigmoid
。
我在模型之间切换的代码中添加了一个 activation="sigmoid"|"softmax"|"hybrid"
参数。我还提供了 scipy 通用最小化器,当成本梯度不在手头时,这可能会有用。
回顾成本函数的工作原理:
成本是所有class项和术语
的所有训练样本的总和-y log (y') - (1-y) log (1-y')
其中 y 是预期响应,即 "y" 训练样本为输入("x" 训练样本)给出的响应。 y' 是预测,即网络及其当前权重和偏差生成的响应。现在,由于预期响应为 0 或 1,因此可以编写单个类别和单个训练样本的成本
-log (y') if y = 1
-log(1-y') if y = 0
因为在第一种情况下 (1-y) 为零,所以第二项消失,在第二种情况下 y 为零,所以第一项消失。 如果
,现在可以说服自己成本很高- 预期响应 y 为 1,网络预测 y' 接近于零
- 预期响应 y 为 0,网络预测 y' 接近 1
换句话说,成本在惩罚错误预测方面起到了作用。现在,如果我们删除第二项 (1-y) log (1-y')
,该机制的一半就消失了。如果预期响应为 1,则低预测仍会产生成本,但如果预期响应为 0,则成本将为零,无论预测如何,特别是高预测(或 假阳性) 会逍遥法外。
现在,因为总成本是所有训练样本的总和,所以存在三种可能性。
所有训练样本都规定 class 为零: 那么成本将完全独立于此 class 的预测,并且无法进行任何学习
一些训练样本将 class 设为零,一些设为一: 然后因为 "false negatives" 或 "misses" 仍然受到惩罚但误报不是网络将找到最小化成本的最简单方法,即不加区别地增加所有样本的 class 的预测
所有训练样本规定 class 是一个: 与第二种情况基本相同,只是这里没有问题,因为这是正确的行为
最后,为什么我们使用 softmax
而不是 sigmoid
会起作用?误报仍然是不可见的。现在很容易看出 softmax 的所有 classes 的总和是 1。因此,如果至少减少另一个 class 以进行补偿,我只能增加一个 class 的预测。特别是,没有假阴性就没有假阳性,而假阴性成本将检测到。
关于如何获得二进制预测:
对于二进制预期响应,四舍五入确实是合适的程序。对于 one-hot,我宁愿找到最大值,将其设置为 1,将所有其他值设置为零。我添加了一个方便的函数,predict
,实现它。
import numpy as np
from scipy import optimize as opt
from collections import namedtuple
# First, a few structures to keep ourselves organized
Problem_Size = namedtuple('Problem_Size', 'Out In Samples')
Data = namedtuple('Data', 'Out In')
Network = namedtuple('Network', 'w b activation cost gradient most_likely')
def get_dims(Out, In, transpose=False):
"""extract dimensions and ensure everything is 2d
return Data, Dims"""
# gracefully acccept lists etc.
Out, In = np.asanyarray(Out), np.asanyarray(In)
if transpose:
Out, In = Out.T, In.T
# if it's a single sample make sure it's n x 1
Out = Out[:, None] if len(Out.shape) == 1 else Out
In = In[:, None] if len(In.shape) == 1 else In
Dims = Problem_Size(Out.shape[0], *In.shape)
if Dims.Samples != Out.shape[1]:
raise ValueError("number of samples must be the same for Out and In")
return Data(Out, In), Dims
def sigmoid(z):
s = 1 / (1 + np.exp(-z))
return s
def sig_cost(Net, data):
A = process(data.In, Net)
logA = np.log(A)
return -(data.Out * logA + (1-data.Out) * (1-logA)).sum(axis=0).mean()
def sig_grad (Net, Dims, data):
A = process(data.In, Net)
return dict(dw = (A - data.Out) @ data.In.T / Dims.Samples,
db = (A - data.Out).mean(axis=1, keepdims=True))
def sig_ml(z):
return np.round(z).astype(int)
def sof_ml(z):
hot = np.argmax(z, axis=0)
z = np.zeros(z.shape, dtype=int)
z[hot, np.arange(len(hot))] = 1
return z
def softmax(z):
z = z - z.max(axis=0, keepdims=True)
z = np.exp(z)
return z / z.sum(axis=0, keepdims=True)
def sof_cost(Net, data):
A = process(data.In, Net)
logA = np.log(A)
return -(data.Out * logA).sum(axis=0).mean()
sof_grad = sig_grad
def get_net(Dims, activation='softmax'):
activation, cost, gradient, ml = {
'sigmoid': (sigmoid, sig_cost, sig_grad, sig_ml),
'softmax': (softmax, sof_cost, sof_grad, sof_ml),
'hybrid': (sigmoid, sof_cost, None, sig_ml)}[activation]
return Network(w=np.zeros((Dims.Out, Dims.In)),
b=np.zeros((Dims.Out, 1)),
activation=activation, cost=cost, gradient=gradient,
most_likely=ml)
def process(In, Net):
return Net.activation(Net.w @ In + Net.b)
def propagate(data, Dims, Net):
return Net.gradient(Net, Dims, data), Net.cost(Net, data)
def optimize_no_grad(Net, Dims, data):
def f(x):
Net.w[...] = x[:Net.w.size].reshape(Net.w.shape)
Net.b[...] = x[Net.w.size:].reshape(Net.b.shape)
return Net.cost(Net, data)
x = np.r_[Net.w.ravel(), Net.b.ravel()]
res = opt.minimize(f, x, options=dict(maxiter=10000)).x
Net.w[...] = res[:Net.w.size].reshape(Net.w.shape)
Net.b[...] = res[Net.w.size:].reshape(Net.b.shape)
def optimize(Net, Dims, data, num_iterations, learning_rate, print_cost = True):
w, b = Net.w, Net.b
costs = []
for i in range(num_iterations):
grads, cost = propagate(data, Dims, Net)
dw = grads["dw"]
db = grads["db"]
w -= learning_rate * dw
b -= learning_rate * db
if i % 100 == 0:
costs.append(cost)
if print_cost and i % 10000 == 0:
print(cost)
return grads, costs
def model(X_train, Y_train, num_iterations, learning_rate = 0.5, print_cost = False, activation='sigmoid'):
data, Dims = get_dims(Y_train, X_train, transpose=True)
Net = get_net(Dims, activation)
if Net.gradient is None:
optimize_no_grad(Net, Dims, data)
else:
grads, costs = optimize(Net, Dims, data, num_iterations, learning_rate, print_cost = True)
Y_prediction_train = process(data.In, Net)
print(Y_prediction_train)
print(data.Out)
print(Y_prediction_train.sum(axis=0))
print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - data.Out)) * 100))
return Net
def predict(In, Net, probability=False):
In = np.asanyarray(In)
is1d = In.ndim == 1
if is1d:
In = In.reshape(-1, 1)
Out = process(In, Net)
if not probability:
Out = Net.most_likely(Out)
if is1d:
Out = Out.reshape(-1)
return Out
def create_data(Dims):
Out = np.zeros((Dims.Out, Dims.Samples), dtype=int)
Out[np.random.randint(0, Dims.Out, (Dims.Samples,)), np.arange(Dims.Samples)] = 1
In = np.random.randint(0, 2, (Dims.In, Dims.Samples))
return Data(Out, In)
train_set_x = np.array([
[1,1,1,1,1],[0,1,1,1,1],[0,0,1,1,0],[0,0,1,0,1]
])
train_set_y = np.array([
[1,0,0],[1,0,0],[0,0,1],[0,0,1]
])
Net1 = model(train_set_x, train_set_y, num_iterations = 20000, learning_rate = 0.001, print_cost = True, activation='sigmoid')
Net2 = model(train_set_x, train_set_y, num_iterations = 20000, learning_rate = 0.001, print_cost = True, activation='softmax')
Net3 = model(train_set_x, train_set_y, num_iterations = 20000, learning_rate = 0.001, print_cost = True, activation='hybrid')
Dims = Problem_Size(8, 100, 50)
data = create_data(Dims)
model(data.In.T, data.Out.T, num_iterations = 40000, learning_rate = 0.001, print_cost = True, activation='softmax')
model(data.In.T, data.Out.T, num_iterations = 40000, learning_rate = 0.001, print_cost = True, activation='sigmoid')
关于如何修复 bug 以及如何将实现扩展到 classify 在更多 classes 之间的想法都可以通过一些维度分析来解决。
我假设您通过 class 验证多个示例意味着多个 classes 而不是多个样本,因为我们需要多个样本来训练甚至 2 classes。
其中 N
= 样本数,D
= 特征数,K
= 类别数(K=2
是一种特殊情况,可以减少这下降到一个维度,即 K=1
和 y=0
表示一个 class 和 y=1
另一个)。数据应具有以下维度:
X: N * D #input
y: N * K #output
W: D * K #weights, also dW has same dimensions
b: 1 * K #bias, also db has same dimensions
#A should have same dimensions as y
维度的顺序可以调换,只要点积正确。
首先处理您的错误:您正在将
W
初始化为N * K
而不是D * K
即。在二进制情况下:w = np.zeros((numberOfTrainingExamples , 1)) #instead of w = np.zeros((numberOfFeatures , 1))
这意味着您唯一一次初始化
W
以更正尺寸是在y
和X
(巧合)具有相同尺寸时。这也会弄乱你的点积:
np.dot(X, w) # or np.dot(w.T,X.T) if you define y as [K * N] dimensions #instead of np.dot(w.T , X)
和
np.dot( X.T, ( A - Y ) ) #np.dot( X.T, ( A - Y ).T ) if y:[K * N] #instead of np.dot( X, ( A - Y ).T )
还要确保成本函数 returns 是一个数字(即不是数组)。
接下来
K>2
你需要做一些改变。b
不再是一个数字,而是一个向量(一维数组)。y
和W
从一维数组变为二维数组。为避免混淆和难以发现的错误,最好将K
、N
和D
设置为不同的值