修改神经网络对单个示例进行分类



这是我在深度学习课程中对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]
])

返回错误:

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))

退货:

array([[ 0.51309252],
[ 0.48646923],
[ 0.50990734]])

同样,当期望[0,1,0]时,将这些值四舍五入到最接近的整数产生向量[1,0,1]

这些是为单个训练示例生成预测的错误操作吗?

您的困难来自于不匹配的维度,所以让我们来解决这个问题,并尝试将它们弄清楚。

您的网络有许多输入、功能,让我们将它们的编号称为N_in(代码中的numberOfFeatures)。它有许多对应于不同类的输出,我们称之为N_out。输入和输出由权重CCD_ 12连接。

现在问题来了。连接是所有对所有的,所以我们需要N_out x N_in输出和输入对中的每一对都有一个权重。因此,在您的代码中,w的形状必须更改为(N_out, N_in)。您可能还希望每个输出都有一个偏移b,所以b应该是大小为(N_out,)(N_out, 1)的向量,这样它就可以很好地处理2d项。

我已经在下面修改过的代码中修复了这个问题,并试图让它变得非常明确。我还抛出了一个模拟数据创建者。

关于一个热门的编码分类输出,我不是神经网络专家,但我认为,大多数人都理解它,所以类是互斥的,所以模拟输出中的每个样本都应该有一个一,其余的为零。

旁注:

有一次,一个相互竞争的答案建议你去掉成本函数中的1-...项。虽然这对我来说是一个有趣的想法,但我的直觉(编辑现在使用无梯度最小化器进行了确认;在下面的代码中使用activation="hybrid"。解算器只需最大化至少在一个训练示例中活动的所有输出。)它不会像那样工作,因为成本将无法惩罚误报(详细解释见下文)。为了让它发挥作用,你必须添加某种正则化。一种似乎有效的方法是使用softmax而不是sigmoid。CCD_ 22之于一热,正如CCD_ 23之于二进制。它确保输出是"模糊一热"。

因此,我的建议是:

  • 如果你想坚持使用sigmoid,而不是明确地强制执行一个热门预测。保持1-...期限
  • 如果您想使用较短的成本函数。强制执行热门预测。例如,使用CCD_ 26而不是CCD_ 27

我在代码中添加了一个activation="sigmoid"|"softmax"|"hybrid"参数,用于在模型之间切换。我还提供了scipy通用最小化器,当成本梯度不在手时,它可能会很有用。

回顾成本函数的工作原理:

成本是术语的所有类别和所有训练样本的总和

-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,则无论预测如何,成本都将为零,特别是高预测(或假阳性)将不受惩罚。

现在,因为总成本是所有训练样本的总和,所以有三种可能性。

  • 所有训练样本都规定类为零:那么成本将完全独立于该类的预测,并且无法进行任何学习

  • 一些训练样本将类归零,一些归一:然后,因为"假阴性"或"未命中"仍然会受到惩罚,但假阳性不是,网络将找到最简单的方法来最小化成本,即不加区别地增加所有样本的类预测

  • 所有培训样本都规定该类为一类:基本上与第二种情况相同,只是在这里没有问题,因为这是的正确行为

最后,为什么我们使用softmax而不是sigmoid?假阳性仍然是不可见的。现在很容易看出,softmax的所有类的和都是一。因此,如果至少减少一个其他类来进行补偿,我只能增加对一个类的预测。特别地,没有假阴性就不可能有假阳性,并且成本将检测到假阴性。

关于如何获得二进制预测:

对于二进制预期响应,四舍五入确实是合适的程序。对于一个热点,我宁愿找到最大的值,将其设置为1,将所有其他值设置为0。我添加了一个方便的函数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以及如何扩展实现以在更多类之间进行分类的想法都可以通过一些维度分析来解决。

我假设你对多个例子进行分类意味着多个类,而不是多个样本,因为我们需要多个样本来训练两个类。

其中,N=样本数量,D=特征数量,K=类别数量(K=2是一种特殊情况,可以将其缩小到一个维度,即K=1y=0表示一个类别,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))
    

    这意味着只有当yX(巧合地)具有相同的维度时,才初始化W以更正维度。

    这也会干扰你的点产品:

    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 )  
    

    还要确保cost函数返回一个数字(即不是数组)。

  • 其次,转到K>2,您需要进行一些更改。b不再是单个数字,而是一个矢量(1D阵列)。CCD_ 48和CCD_。为了避免混淆和难以发现的错误,最好将KND设置为不同的值

相关内容

  • 没有找到相关文章

最新更新