如何提高神经网络的准确性

How to increase accuracy of neural networks

我正在尝试构建一个简单的神经网络来将产品图像分类到不同的标签(产品类型)。即,给定一张新产品图片,告诉它属于哪个产品类别类型(书籍、玩具、电子产品等)。

我在每个产品编号下都有几个产品图片,每个产品编号在 excel sheet 中都有一个标签(即产品类型)。

下面是我的代码:

from sklearn.preprocessing import LabelEncoder
from sklearn.cross_validation import train_test_split
from keras.models import Sequential
from keras.layers import Activation
from keras.optimizers import SGD
from keras.layers import Dense
from keras.utils import np_utils
from imutils import paths
import numpy as np
import argparse
import cv2
import os
import xlwt
import xlrd
import glob2
import pickle

def image_to_feature_vector(image, size=(32,32)):
    return cv2.resize(image, size).flatten()

def read_data(xls = "/Desktop/num_to_product_type.xlsx"):
    book = xlrd.open_workbook(xls)
    sheet = book.sheet_by_index(0)
    d = {}
    for row_index in xrange(1, sheet.nrows): # skip heading row
        prod_type, prod_num = sheet.row_values(row_index, end_colx=2)
        prod_type = unicode(prod_type).encode('UTF8')
        produ_num = unicode(prod_num).encode('UTF8')

        d[prod_num] = prod_type
    return d

def main():

    try:
        imagePaths=[]
        print("[INFO] describing images...")
        for path, subdirs, files in os.walk(r'/Desktop/data'):
            for filename in files:
                imagePaths.append(os.path.join(path, filename))

        files = glob2.glob('/Desktop/data/**/.DS_Store')
        for i in files:
            imagePaths.remove(i)  
    except:
        pass

    dd = read_data() 
    # initialize the data matrix and labels list
    data = []
    labels1 = []

    for (i, imagePath) in enumerate(imagePaths):
        image = cv2.imread(imagePath)
        #print(image.shape)
        subdir = imagePath.split('/')[-2]
        for k, v in dd.items():
            if k == subdir:
                label = v
                break

        features = image_to_feature_vector(image)
        data.append(features)
        labels1.append(label)


        # show an update every 1,000 images
        if i > 0 and i % 1000 == 0:
            print("[INFO] processed {}/{}".format(i, len(imagePaths)))
    print("String Labels")
    print(labels1)

    # encode the labels, converting them from strings to integers
    le = LabelEncoder()
    labels = le.fit_transform(labels1)
    print(labels) 

    d={}
    d[labels[0]] = labels1[0]

    for i in range(1,len(labels)-1):
        if labels[i-1] != labels[i] and labels[i] == labels[i+1]:
            d[labels[i]]  = labels1[i]

    data = np.array(data) / 255.0
    labels = np_utils.to_categorical(labels, 51)
    print("To_Categorical")
    print(labels) 

    print("[INFO] constructing training/testing split...")
    (trainData, testData, trainLabels, testLabels) = train_test_split(
        data, labels, test_size=0.25, random_state=42)

    model = Sequential()
    model.add(Dense(768, input_dim=3072, init="uniform",
        activation="relu"))
    model.add(Dense(384, init="uniform", activation="relu"))
    model.add(Dense(51))
    model.add(Activation("softmax"))

    print("[INFO] compiling model...")

    sgd = SGD(lr=0.125
              )
    model.compile(loss="categorical_crossentropy", optimizer=sgd,
        metrics=["accuracy"])
    model.fit(trainData, trainLabels, nb_epoch=50, batch_size=750)


#     #Test the model
    #show the accuracy on the testing set
     print("[INFO] evaluating on testing set...")
     (loss, accuracy) = model.evaluate(testData, testLabels,
         batch_size=128, verbose=1)
     print("[INFO] loss={:.4f}, accuracy: {:.4f}%".format(loss,
         accuracy * 100))

if __name__ == '__main__':

    main()

神经网络是一个3-2-3-51前馈神经网络。第 0 层包含 3 个输入。第 1 层和第 2 层是分别包含 2 和 3 个节点的隐藏层。第 3 层是具有 51 个节点的输出层(即,对于 51 个产品类别类型)。然而,有了这个我得到的准确度非常低,只有大约 45-50%。

我做错了什么吗?如何提高神经网络的准确性?我在某处读到它可以通过“crossvalidation and hyperparameter tuning”来完成,但它是如何完成的?抱歉,我是神经网络的新手,只是想尝试一些新东西。谢谢

超参数验证

您为什么选择 3-2-3-2 ANN 而不是 3-6-23-4-4-4-4-2

通常我们不知道达到 80% 准确率所需的确切拓扑结构(层数、每层神经元数、神经元之间的连接)或任何让我们满意的东西。这就是超参数训练发挥作用的地方。有了它,我们告诉我们的程序尝试几种不同的拓扑,直到它找到一个对我们来说足够好的。

您如何告诉您的程序要尝试哪些拓扑结构?我们用另一个 ANN 或进化算法来实现,它生成伪随机拓扑,评估每个拓扑,并为每个拓扑打分,然后将得分较高的拓扑组合起来,好吧,你知道它是如何工作的。

这样做肯定会帮助你提高总分(前提是你的问题有好的解决方案)

交叉验证

你怎么知道你的算法要进行多少次迭代?你的停止标准是什么?

ANN 有一个反复出现的问题,称为记忆。如果你 运行 你的学习算法进行 100 万次迭代,你通常会比你 运行 只进行 10 次迭代得到更好的分数,但这可能是由于你对训练集的记忆:你 ANN 学习只预测那些训练集的结果,但尝试预测它以前没有见过的数据会做得很差。

解决该问题的一种方法是交叉验证,这意味着您将把数据分成两组:trainvalidation。然后你用你的 train 集训练你的 ANN only 你想要的迭代次数,但同时,你将用 validation 集测试你的 ANN 知道什么时候停止。如果在 10 次迭代后你的 train 准确率持续上升,但你的 validation 准确率持续下降,那么你可以确定你的 ANN 正在记忆,所以你将停止你的学习算法并选择原来的 ANN 10 次迭代前。

当然,10只是一个例子,你可以尝试不同的值,甚至把它放在你的超参数训练中,你不需要硬编码这个值。

我建议你看一下这个 course in Coursera 的资料,他们对这些概念的解释非常清楚。

(顺便说一句:通常您将输入集分成 3 个:trainvalidatetest。其中 test 用于查看您的 ANN 的行为方式对于完全看不见的数据,您不会在训练中使用 test 集来做出 任何 决定)

为了在 keras 中创建图像分类器,我建议尝试使用卷积神经网络,因为它们对图像的处理效果往往更好。此外,层之间的归一化有助于提高训练期间的准确性,这应该有助于产生更好的 validation/test 准确性。 (与训练前归一化数据的概念相同。)

对于 keras 卷积层,只需调用 model.add(Conv2D(params)) 并在层之间进行归一化,您可以调用 model.add(BatchNormalization())

卷积神经网络更先进但更适合图像。不同之处在于,卷积在高层次上只是 "mini" 扫描图像块的神经网络。这很重要,因为例如你可以在两个图像中有 EXACT 相同的对象,但是如果它们在该图像中的不同位置,则正常的神经网络会将其视为两个不同的对象与图像中不同位置的同一对象...

所以这个 "mini" 扫描图像块(通常称为内核大小)的神经网络更倾向于选择类似的 特征 个对象。然后将对象特征训练到网络中,因此即使对象出现在图像的不同区域,也可以更准确地将其识别为同一事物。这就是为什么卷积神经网络更适合处理图像的关键。

这是 keras 2 中的一个基本示例,其标准化基于 NVIDIA 模型架构...

        model = Sequential()
        # crop the images to get rid of irrelevant features if needed...
        model.add(Cropping2D(cropping=((0, 0), (0,0)), input_shape=("your_input_shape tuple x,y,rgb_depth")))
        model.add(Lambda(lambda x: (x - 128) / 128)) # normalize all pixels to a mean of 0 +-1
        model.add(Conv2D(24, (2,2), strides=(2,2), padding='valid', activation='elu')) # 1st convolution
        model.add(BatchNormalization()) # normalize between layers
        model.add(Conv2D(36, (2,2), strides=(2,2), padding='valid', activation='elu')) # 2nd convolution
        model.add(BatchNormalization())
        model.add(Conv2D(48, (1,1), strides=(2,2), padding='valid', activation='elu')) # 3rd convolution
        model.add(BatchNormalization())
        # model.add(Conv2D(64, (3,3), strides=(1,1), padding='valid', activation='elu')) # 4th convolution
        # model.add(BatchNormalization())
        # model.add(Conv2D(64, (3,3), strides=(1,1), padding='valid', activation='elu')) # 4th convolution
        # model.add(BatchNormalization())
        model.add(Dropout(0.5))
        model.add(Flatten()) # flatten the dimensions
        model.add(Dense(100, activation='elu')) # 1st fully connected layer
        model.add(BatchNormalization())
        model.add(Dropout(0.5))
        model.add(Dense(51, activation= 'softmax')) # label output as probabilites

最后,超参数调整只是调整批量大小、epoch、学习率等以获得最佳结果。您所能做的就是进行实验,看看哪种效果最好。