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

时间:2017-11-15 13:04:53

标签: python numpy tensorflow neural-network linear-algebra

这是我从深度学习课程中的一个Andrew NG的神经网络的自定义扩展,而不是为二进制分类生成0或1我正在尝试 分类多个例子。

输入和输出都是热编码的。

如果训练不多,我会收到<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <c:forEach> </c:forEach> </body> </html>

的准确度

如何对单个训练样例进行分类,而不是对所有训练样例进行分类?

我认为我的实现中存在一个错误,因为此网络的问题是训练示例(train_set_x)和输出值(train_set_y)都需要具有相同的维度或者接收到与矩阵的维度相关的错误。 例如使用:

'train accuracy: 67.51658067499625 %'

返回错误:

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:操作数无法与形状(3,3)(1,4)

一起广播

网络代码:

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

更新:此实现中存在一个错误,即训练示例对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) 必须包含相同的维度。可以指出如何修改线性代数的方向吗?

更新2:

我修改了@Paul Panzer的答案,以便学习率为0.001且train_set_x,train_set_y对是唯一的:

(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]添加到numpy数组并进行转置):

[0,0,1,1,0]

返回:

print(sigmoid(dw @ np.array([[0,0,1,1,0]]).T + db))

同样,当预期array([[ 0.51309252], [ 0.48646923], [ 0.50990734]]) 时,将这些值舍入为最接近的整数会产生向量[1,0,1]

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

2 个答案:

答案 0 :(得分:5)

你的困难来自于不匹配的维度,所以让我们一起解决问题并尝试直接解决问题。

您的网络有许多输入功能,我们可以在您的代码中拨打N_innumberOfFeatures)。它有许多输出,对应于不同的类别,我们可以调用它们的号码N_out。输入和输出通过权重w连接。

现在问题就在于此。连接是全部的,因此我们需要每个N_out x N_in对输出和输入的权重。因此,在您的代码中,w的形状必须更改为(N_out, N_in)。您可能还希望每个输出都有一个偏移b,因此b应该是大小为(N_out,)的向量,或者更确切地说是(N_out, 1),因此它可以很好地与2d项一起使用。

我已修复了以下修改后的代码,并尝试将其设为非常明确。我还把一个模拟数据创建者扔进了讨价还价。

重新编写一个单独编码的分类输出,我不是神经网络的专家,但我认为,大多数人都理解它,因此类是互斥的,因此模拟输出中的每个样本都应该有一个,其余的零。

旁注:

在某一时刻,竞争性答案建议您删除成本函数中的1-...项。虽然这对我来说似乎是一个有趣的想法我的直觉(编辑现在使用无梯度最小化器确认;在下面的代码中使用activation =&#34; hybrid&#34; Solver将简单地最大化至少在一个训练示例中有效的所有输出。)它是否会像那样工作,因为成本将无法惩罚误报(详见下文)。为了使它工作,你必须添加某种正规化。一种似乎有用的方法是使用softmax而不是sigmoidsoftmaxsigmoid对二进制文件的热度。它确保输出是模糊的一热&#34;。

因此我的建议是:

  • 如果您想坚持sigmoid并且没有明确强制执行单一预测。保留1-...字词。
  • 如果您想使用较短的费用功能。实施一个热门的预测。例如,使用softmax代替sigmoid

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

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

费用是所有课程和术语

的所有培训样本的总和
-y log (y') - (1-y) log (1-y')

其中y是预期的响应,即&#34; y&#34;输入的训练样本(&#34; x&#34;训练样本)。 ý&#39;是预测,网络及其当前权重和偏差产生的响应。现在,因为预期的响应是0或1,所以可以写出单个类别和单个训练样本的成本

-log (y')   if   y = 1
-log(1-y')  if   y = 0

因为在第一种情况下(1-y)为零,所以第二项消失,在第二种情况下y为零,因此第一项消失。 现在可以说服自己,如果

,成本很高
  • 预期响应y为1且网络预测y&#39;接近零
  • 预期响应y为0且网络预测y&#39;接近一个

换句话说,成本在惩罚错误预测方面发挥了作用。现在,如果我们放弃第二个术语(1-y) log (1-y'),这个机制的一半就消失了。如果预期响应为1,则低预测仍将产生成本,但如果预期响应为0,则无论预测如何,成本将为零,特别是高预测(或误报)将不受惩罚。

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

  • 所有训练样本都规定该等级为零:  然后费用将完全独立于本课程的预测,并且不会进行任何学习

  • 一些训练样本将该类置于零,一些在一个:  然后因为&#34;假阴性&#34;或者&#34;未命中&#34;仍然会受到惩罚,但误报并不是网络会找到最简单的方法来最大限度地降低成本,即不加区分地增加所有样本的类别预测

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

最后,如果我们使用softmax代替sigmoid,为什么会有效呢?误报仍然是隐形的。现在很容易看出softmax的所有类的总和是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') 

答案 1 :(得分:3)

如何修复bug以及如何扩展实现以在更多类之间进行分类的想法都可以通过一些维度分析来解决。

我假设你通过分类多个例子来表示多个类而不是多个样本,因为我们需要多个样本来训练即使是2个类。

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

    这意味着,只有当Wy(巧合)具有相同尺寸时,才会初始化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 )  
    

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

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