搭建自己的深度学习神经网络

上篇文章,自己推导了一遍误差反向传播算法,从而对深度学习有了一定的认识。这期试着自己搭建一个基于python的深度学习神经网,来解决一定的问题。


需要用到:python3、numpy

数据集:MNIST数据集(一个被”嚼烂”了的数据集, 很多教程都会对它”下手”, 几乎成为一个 “典范”)

MNIST 数据集可在 http://yann.lecun.com/exdb/mnist/ 获取, 它包含了四个部分:

Training set images: train-images-idx3-ubyte.gz (9.9 MB, 解压后 47 MB, 包含 60,000 个样本)

Training set labels: train-labels-idx1-ubyte.gz (29 KB, 解压后 60 KB, 包含 60,000 个标签)

Test set images: t10k-images-idx3-ubyte.gz (1.6 MB, 解压后 7.8 MB, 包含 10,000 个样本)

Test set labels: t10k-labels-idx1-ubyte.gz (5KB, 解压后 10 KB, 包含 10,000 个标签)

MNIST 数据集来自美国国家标准与技术研究所, National Institute of Standards and Technology (NIST). 训练集 (training set) 由来自 250 个不同人手写的数字构成, 其中 50% 是高中学生, 50% 来自人口普查局 (the Census Bureau) 的工作人员. 测试集(test set) 也是同样比例的手写数字数据。

我们创建一个DataParser.py文件来解析MNIST数据:


def load_mnist(path, kind='train'):

labels_path = os.path.join(path, '%s-labels-idx1-ubyte' % kind)

images_path = os.path.join(path, '%s-images-idx3-ubyte' % kind)

with open(labels_path, 'rb')as lbfile:

magic, n = struct.unpack('>II', lbfile.read(8))

labels = np.fromfile(lbfile, dtype=np.uint8)

with open(images_path, 'rb')as imgfile:

magic, num, rows, cols = struct.unpack('>IIII', imgfile.read(16))

images = np.fromfile(imgfile, dtype=np.uint8).reshape(len(labels), 784)

return images /255, labels

除以255把图片像素值映射到[0,1]上。
手写图片的尺寸是28*28


训练思路:
我们将训练过程分为60个epochs,每个epoch里训练1000条数据,训练结束后用测试数据集来计算训练的准确率。
每次测试时,我们需要打乱测试数据,以保证测试结果更准确。
确定网络参数:
由于深度学习需要包括至少2个隐层,所以我们将网络结构定义如下:
数据层——全连接层1——隐层1——全连接层2——隐层2——输出层
数据层、全连接层1各有784个神经元(对应手写图片上的28*28个像素),全连接层2包含256个,输出层10个(对应0—9的概率)
激活函数采用Sigmoid函数,损失函数采用交叉熵损失函数(交叉熵损失函数的导数分母有一个1-x项,尔Sigmoid函数的导数分子有一个1-x项,两者相乘的时候完美解决了使用Sigmoid函数作为激活函数而产生的梯度发散问题)
下面我们就可以用python来实现这个网络了。


数据层定义如下:(数据层正向传播输出其本身,且无需反向传播)

class DataLayer:

    def __init__(self):
        imgs, lbls = dp.load_mnist('', kind='train')
        self.x = imgs
        self.y = lbls
        self.pos = 0

    def forward(self, index):
        pos = index
        xx = self.x[pos]
        ret = (xx.reshape(xx.size,1), self.y[pos])
        return ret

    def backward(self, d):
        pass

测试数据层定义如下:(使用shuffle_data每次打乱数据)

class TestLayer:

    def __init__(self):
        imgs, lbls = dp.load_mnist('', kind='t10k')
        self.x = imgs
        self.y = lbls

    def shuffle_data(self):
        l = len(self.x)
        index = list(range(l))
        np.random.shuffle(index)
        self.x = self.x[index]
        self.y = self.y[index]

    def forward(self, index):
        xx = self.x[index]
        ret = (xx.reshape(xx.size,1), self.y[index])
        return ret

全连接层如下:(权重除以一个值np.sqrt(l_x),会避免产生异常数据)
向前传播forward:矩阵运算,计算z值
反向传播backward:根据接收到的误差更新权重和偏置

class FullConnect:

    def __init__(self, l_x, l_y):
        self.weights = np.random.randn(l_y, l_x) / np.sqrt(l_x)
        self.bias = np.random.randn(l_y, 1)
        self.lr = 0

    def forward(self, x):
        self.x = x
        self.y = np.dot(self.weights, x)+self.bias
        return self.y

    def backward(self, d):
        self.dw = np.dot(d, self.x.T)
        self.db = d
        self.dx = np.dot(self.weights.T, d)

        self.weights -= self.lr * self.dw
        self.bias -= self.lr * self.db

        return self.dx

激活函数:

class Sigmoid:

    def __init__(self):
        pass

    def sigmoid(self, x):
        return 1 / (1 + np.exp(-x))

    def forward(self, x):
        self.x = x
        self.y = self.sigmoid(x)
        return self.y

    def backward(self, d):  
        sig = self.sigmoid(self.x)
        self.dx =d * sig * (1 - sig)
        return self.dx  # 反向传递梯度

损失函数:

class CrossEntropyLoss:
    def __init__(self):
        pass

    def forward(self, x, label):
        self.x = x
        self.label = np.zeros_like(x)
        self.label[label]=1.0

        self.loss=np.nan_to_num(-self.label * np.log(x) -(1- self.label)*np.log(1-x))
        self.loss=np.sum(self.loss) / x.shape[0]
        return  self.loss

    def backward(self):
        self.dx=(self.x-self.label)/self.x /(1- self.x)
        return self.dx

确定准确度的方法为:(数组x中最大值(最大概率)的下标和标记值一样,我们认为结果准确,反之不正确)

class Accuracy:
    def __init__(self):
        pass

    def forward(self, x, label):
        b = x.max()
        if x[label] > b-0.0001:
            return 1
        else:
            return 0

然后我们将这些层进行简单的组装,就可以进行训练了:

def train():
    block_size = 1000
    datalayer1 = DataLayer()
    datalayer2 = TestLayer()

    inner_layers = []
    inner_layers.append(FullConnect(784, 256))
    inner_layers.append(Sigmoid())
    inner_layers.append(FullConnect(256, 10))
    inner_layers.append(Sigmoid())

    lostlayer = CrossEntropyLoss()
    accuracy = Accuracy()
    epochs = 60

    for layer in inner_layers:
        layer.lr = 0.05

    for i in range(epochs):
        print('epochs: ', i)
        losssum = 0
        iters = 0

        for j in range(block_size):
            losssum = 0
            iters += 1
            x, label = datalayer1.forward(i * block_size +j)

            for layer in inner_layers:
                x = layer.forward(x)

            loss = lostlayer.forward(x, label)
            losssum += loss
            d = lostlayer.backward()

            for layer in inner_layers[::-1]:
                d = layer.backward(d)

            if j == block_size-1:
                accu = 0
                datalayer2.shuffle_data()
                for k in range(10000):
                    x, label = datalayer2.forward(index=k)
                    for layer in inner_layers:
                        x = layer.forward(x)
                    accu += accuracy.forward(x, label)
                print('accuracy: ', accu/10000)

我们把学习速度定位0.05,然后经过不到2分钟的训练,就能将准确率提升到96%了。输出数据我们可以看到,第一回合到第二回合性能上有了很大的提升。

epochs:  0
accuracy:  0.6473
epochs:  1
accuracy:  0.846
epochs:  2
accuracy:  0.8799
epochs:  3
accuracy:  0.8828
epochs:  4
accuracy:  0.8964
epochs:  5
accuracy:  0.8988
epochs:  6
accuracy:  0.9022
epochs:  7
accuracy:  0.8958
epochs:  8
accuracy:  0.9058
epochs:  9
accuracy:  0.9157
epochs:  10
accuracy:  0.9055
epochs:  11
accuracy:  0.915
epochs:  12
accuracy:  0.9149
epochs:  13
accuracy:  0.9114
epochs:  14
accuracy:  0.9259
epochs:  15
accuracy:  0.9226
epochs:  16
accuracy:  0.9308
epochs:  17
accuracy:  0.9304
epochs:  18
accuracy:  0.9394
epochs:  19
accuracy:  0.9323
epochs:  20
accuracy:  0.9328
epochs:  21
accuracy:  0.9356
epochs:  22
accuracy:  0.94
epochs:  23
accuracy:  0.9456
epochs:  24
accuracy:  0.9431
epochs:  25
accuracy:  0.9374
epochs:  26
accuracy:  0.9466
epochs:  27
accuracy:  0.9468
epochs:  28
accuracy:  0.9465
epochs:  29
accuracy:  0.9389
epochs:  30
accuracy:  0.9471
epochs:  31
accuracy:  0.9473
epochs:  32
accuracy:  0.9524
epochs:  33
accuracy:  0.953
epochs:  34
accuracy:  0.9501
epochs:  35
accuracy:  0.944
epochs:  36
accuracy:  0.9458
epochs:  37
accuracy:  0.9554
epochs:  38
accuracy:  0.9556
epochs:  39
accuracy:  0.9536
epochs:  40
accuracy:  0.9497
epochs:  41
accuracy:  0.9557
epochs:  42
accuracy:  0.9548
epochs:  43
accuracy:  0.9517
epochs:  44
accuracy:  0.9496
epochs:  45
accuracy:  0.9495
epochs:  46
accuracy:  0.9579
epochs:  47
accuracy:  0.9573
epochs:  48
accuracy:  0.9485
epochs:  49
accuracy:  0.9605
epochs:  50
accuracy:  0.9618
epochs:  51
accuracy:  0.9625
epochs:  52
accuracy:  0.9599
epochs:  53
accuracy:  0.9574
epochs:  54
accuracy:  0.961
epochs:  55
accuracy:  0.9621
epochs:  56
accuracy:  0.9597
epochs:  57
accuracy:  0.9565
epochs:  58
accuracy:  0.9591
epochs:  59
accuracy:  0.9604

到此,通过搜集网上各种资料,自己实现了第一个深度学习神经网络,中间不免有不合理的地方,继续加油!!!!

你可能感兴趣的:(搭建自己的深度学习神经网络)