fengchuan 发表于 2021-1-25 16:17

从零开始写一个神经网络(第一篇)

本帖最后由 fengchuan 于 2021-1-25 16:19 编辑

# 从零开始写一个神经网络(第一篇)

>​      本系列是根据哔哩哔哩up**大野喵渣**的系列教程《从零开始写一个神经网络》所做的学习笔记,起初看到这个系列教程是在19年,但是当时由于一些知识不懂,就搁置了。这次已经跟着做完了大部分环节,现在就将自己的理解和课上的代码做成笔记,记录下来~
>
>​      由于都是我自己粗浅的理解,如果想要理解的更详细一些,真的建议大家去看一看这个系列视频,不仅仅是能学到怎么做一个神经网络,在这个过程中,你更能学习到up是怎么思考的,代码是怎么写的,up的代码写的真的太好了!!强烈推荐!!
>
>哔哩哔哩视频地址:https://space.bilibili.com/28496477/channel/detail?cid=75370
>
>> 本教程全部采用python进行编程

## 目标

​      训练一个识别手写数字的模型

## 所需知识

- python基本语法

## 准备工作

### 数据集

#### 数据获取

​      数据集是从(http://yann.lecun.com/exdb/mnist/)这个网站上下载的,数据集共包含60,000个例子的训练集、10,000个例子的测试集。此外,网站还介绍了根据这个样本,很多种的学习框架的正确率。

#### 数据格式

​      根据网站的介绍,有四个数据文件,分别为两个数据和两个标签。下面是网站关于数据的介绍,这里我只选取了训练集,测试集同理。

**TRAINING SET LABEL FILE (train-labels-idx1-ubyte):**

```
      `
`0000   32 bit integer 0x00000801(2049) magic number (MSB first)`
`0004   32 bit integer 60000      number of items`
`0008   unsigned byte??      label`
`0009   unsigned byte??      label`
`........`
`xxxx   unsigned byte??      label
The labels values are 0 to 9.
```

**TRAINING SET IMAGE FILE (train-images-idx3-ubyte):**

```
      `
`0000   32 bit integer 0x00000803(2051) magic number`
`0004   32 bit integer 60000      number of images`
`0008   32 bit integer 28      number of rows`
`0012   32 bit integer 28      number of columns`
`0016   unsigned byte??      pixel`
`0017   unsigned byte??      pixel`
`........`
`xxxx   unsigned byte??      pixel
```

Pixels are organized row-wise. Pixel values are 0 to 255. 0 means background (white), 255 means foreground (black).

​      根据介绍,可以知道文件并不是以图片的形式进行存储的,而是以字节的形式进行存储。在LABEL SET中,0-3字节表示magic number(magic number表示幻数,可以理解为一种标记,但是具体是做什么的我还不太清楚),4-7字节表示数据的大小,在TRAINING SET LABEL中则是60,000,后面每一个字节表示一个标签;在IMAGE SET中,0-3字节表示magic number,4-7字节表示数据的大小,8-11字节和12-15字节分别表示图片所占的行数和列数,即28*28的图片。

​      既然我们已经知道了数据的格式,那么下面就来用代码处理数据吧

#### 数据处理

​      首先我们将数据下载到电脑上,解压之后得到四个文件,我们引用Path这个包进行数据的处理,为什么要引用path这个包呢?通过下面的代码我们可以看出path这个包将目录结构展示的很清楚,路径更加直观的表现出来。

```python
from pathlib import Path
#数据集的路径
dataset_path = Path('./MNIST') #数据集路径
train_img_path = dataset_path/'train-images.idx3-ubyte' #训练集图片路径
train_lab_path = dataset_path/'train-labels.idx1-ubyte' #训练集label路径
test_img_path = dataset_path/'t10k-images.idx3-ubyte' #测试集图片路径
test_lab_path = dataset_path/'t10k-labels.idx1-ubyte' #测试集label路径
```

​                获取到路径之后,就是数据的读入,这里定义了三个变量 train_num、valid_num和test_num,将数据划分为训练集、验证集和测试集,也就是在原数据的基础上,中间添加了一个验证集,为什么要中间添加一个验证集呢?每一个数据集的作用又是什么呢?等我们把数据处理完之后,我们再来说一说这个问题。

​      要处理数据,我们要知道我们需要那些数据。首先,前面的magic number、数据集的大小和图片的大小其实我们是可以不需要的,我们在代码里直接写死就好。这里引用了struct包进行数据的解包,**struct.unpack('>4i',f.read(16))**中,'>'表示数据是按照大端存储的,即较低的有效字节存放在较高的存储器地址中,较高的有效字节存放在较低的存储器地址,下面我也放了一个大小端存储的例子,采用大端存储更符合人们的阅读习惯,这里就不详细展开了,如果想要更加深入的了解,请参考我最后面的参考文献。'4'表示读取4个。'i'表示integer。这样我们就把前面用不到的数据都出来了,不需要对这些数据做什么处理。

| 地址偏移 | 大端模式 | 小端模式 |
| -------- | -------- | -------- |
| 0x00   | 12       | 78       |
| 0x01   | 34       | 56       |
| 0x02   | 56       | 34       |
| 0x03   | 78       | 12       |

​      接下来就是读取真正有用的数据,以train image为例,这里用numpy进行了数据处理操作,将读取的数据变成了一个1x784的矩阵,后面我们计算要用到。

```python
import struct
#载入数据集
train_num = 50000 #训练集大小
valid_num = 10000 #验证集大小
test_num = 10000 #测试集大小

with open(train_img_path,'rb') as f:
    struct.unpack('>4i',f.read(16))
    temp_img = np.fromfile(f,dtype=np.uint8).reshape(-1,28*28)
    train_img = temp_img[:train_num]
    valid_img = temp_img
   
with open(test_img_path,'rb') as f:
    struct.unpack('>4i',f.read(16))
    test_img = np.fromfile(f,dtype=np.uint8).reshape(-1,28*28)
   
with open(train_lab_path,'rb') as f:
    struct.unpack('>2i',f.read(8))
    temp_lab = np.fromfile(f,dtype=np.uint8)
    train_lab = temp_lab[:train_num]
    valid_lab = temp_lab
   
with open(test_lab_path,'rb') as f:
    struct.unpack('>2i',f.read(8))
    test_lab = np.fromfile(f,dtype=np.uint8)
```

​      这样,我们就成功的把数据全部都读取出来了,可以用matplotlib画出来看看数据是什么样子的~

```python
import matplotlib.pyplot as plt
#展示数据集
def show_train(index):
    plt.imshow(train_img.reshape(28,28),cmap='gray')
    print('label:{}'.format(train_lab))
   
def show_valid(index):
    plt.imshow(valid_img.reshape(28,28),cmap='gray')
    print('label:{}'.format(valid_lab))
   
def show_test(index):
    plt.imshow(test_img.reshape(28,28),cmap='gray')
    print('label:{}'.format(test_lab))
```



​      到这我们就把数据集拿到了,后面就可以从train_img、train_lab中取数据进行后面的运算操作了。然后我们再来看一下刚才留下的问题,问什么要把训练集再分成两份,变成一个测试集和一个验证集,验证集的作用是什么呢?

​      训练集,顾名思义就是用来训练我们最后写好的神经网络框架的数据集,让框架根据训练集的数据更好的调整自己的参数,得到一个对训练集拟合度更高的模型。

​      验证集,就是我们知道标签,只给训练好的框架图片,让他进行识别,验证集不用来调整参数

​      测试集,用来最终测试框架的正确率。

​      举个例子来说比如我们的框架就是一个学生,我们让这个框架做的事情就是学习课本上的知识,训练集就相当于一个课本,我们不断地让他去学习这一个课本,随着学习次数的增加,他对课本的掌握程度肯定会不断提高(当然他的掌握程度提高是我们非常乐意看到的事情),当经过几次的训练之后,我们再去用课本对他进行考试,他的正确率就会变得很高。但是这并不能反映出他学习的真实水平,因为还有一种可能是因为他把整个书本都背诵下来了(这就是发生了过拟合现象),我们再去用这本书考他就没有多大的意义。这时候老师们就发明了小测验,找一个课本上没有出现过的题目,让这个学生来进行回答,这个时候学生是不知道答案的,他需要根据他所学的知识来进行作答,这样作答的结果更能反映他学习的成果。

​      这里的小测验就是相当于验证集,我们知道正确的答案,但是模型不知道,我们需要根据模型给出的答案来进行判断他的学习成果怎么样,但是这个结果也不能作为模型评价好坏的标准。假如参与测验的学生有好几个(对应不同的训练框架)根据你出的题目,每个学生进行作答,可能某个学生恰好很会瞎猜,恰好你出的题目他全都猜对了,在你的主观感觉里这个学生他就是个学习好的学生,但是其实他也只是恰好猜得准。所以我们还需要测试集,也就是在最后再出一个考试,把你的主观情感也排除,你也不知道答案,就让学生去做答,然后和正确答案进行比对,这样才能更减少主观带来的影响。

​      过拟合现象,在我们上述的例子中,就是指同学将课本的知识全部都背诵了下来,对书本的问题回答得非常的好,但是一做测验,就什么都不会了,这就是发生了过拟合现象,并没有掌握真正的脉络和本质。

​      好了,上面就是我对数据处理的一些总结。

​      

## 参考文献

1. 大小端 https://blog.csdn.net/lis_12/article/details/52698634
2. python之struct详解https://blog.csdn.net/qq_30638831/article/details/80421019
3. Python使用struct处理二进制(pack和unpack用法 https://blog.csdn.net/jackyzhousales/article/details/78030847
4. numpy.reshape(-1,1)https://blog.csdn.net/qq_42804678/article/details/99062431

Johan_000 发表于 2021-1-26 09:42

这个就是那本神经网络入门那本书上写的东西吧,这个训练真的看脸的,玄学炼丹。而且要实际写出来还是有点麻烦的,当初学着搓了一个结果准确率惨不忍睹。
这个神经网络规模还算小的了,用来学习基本神经网络原理还是不错的。

garfieldlong 发表于 2021-1-25 16:23

看看,楼主加油。

coolsnake 发表于 2021-1-25 16:40

谢谢楼主,写得挺好

king1299 发表于 2021-1-25 16:48

感觉没能理解。。。。脑子不够用

lanshushu 发表于 2021-1-25 16:50

啥也不说就是牛逼

fengchuan 发表于 2021-1-25 16:52

king1299 发表于 2021-1-25 16:48
感觉没能理解。。。。脑子不够用

哪里没理解,可以留言我们讨论下:lol

coolcalf 发表于 2021-1-25 17:03

楼主加油
以后时间合适,也学习一下。

孺子牛_Boil 发表于 2021-1-25 17:12

这个真从零开始了 简直胎教

vethenc 发表于 2021-1-25 18:22

写的挺好

ciker_li 发表于 2021-1-25 20:43

不错不错,学习学习
页: [1] 2
查看完整版本: 从零开始写一个神经网络(第一篇)