顺藤摸瓜-mnist数据集的补充,二进制buffer读取,numpy的frombuffer方法等

方便的Numpy查询手册
https://docs.scipy.org/doc/numpy/search.html

先是numpy.frombuffer
https://docs.scipy.org/doc/numpy/reference/generated/numpy.frombuffer.html?highlight=frombuffer#numpy.frombuffer
然后是gzip
https://docs.python.org/3.6/library/gzip.html

还原遇到这个问题的全程,这个问题还是第一次遇到,脑补的是被自动喂食机喂习惯了,突然只给食用说明,用不来筷子叉子的既视感。
原问题起始于 https://blog.csdn.net/qq_42731466/article/details/83473112

好,正文开始,

整个事情是从frombuffer触发的,要解决的如下:

1.t10k-images.idx3-ubyte二进制文件的读取问题
2.frombuffer问题,cachebuffer是有区别的

0.还原当时的问题

如下的步骤就是触发这个小文的顺序。
.
.

  • numpy.frombuffer文档
    在这里插入图片描述

  • 调试

然后调试它的代码

s = 'hello world'
a=np.frombuffer(s, dtype='S1', count=5, offset=6)

.结果如下

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute '__buffer__'

解决方式是:

s = b'Hello World'

在字符串前面加上b,在PY3中,默认字符串类型是unicode。 b用于创建和显示字节串。

1.ubyte的读取

以上是解释,现在继续触发问题,unicode和编码的问题,以及提到的b

先略说b的问题,下面详细说说编码,因为篇幅较大。

前面说到了 在py3中 字符串的默认类型是 unicode,不加b是一个字符串对象,加了就是一个字节流
在这个实际的代码中,那么为什么会出现这种问题。这就要回到解析的文件本身来看

在代码中有两句很关键,这两句分别对图像和标签先解压再进行解析
F112触发F1121F1122
在这里插入图片描述

在计算机中,所有的文件都是二进制文件,但是不同的文件有不同的解析方式,我们需要知道这个文件应该怎么解析,注意到它的文件名t10k-images.idx3-ubyte,格式idx3-ubyte暗示了。

事实上,文件的提供者明确说明了这个问题
http://yann.lecun.com/exdb/mnist/
These files are not in any standard image format. You have to write your own (very simple) program to read them. The file format is described at the bottom of this page.
对文件的说明如下:
The data is stored in a very simple file format designed for storing vectors and multidimensional matrices. General info on this format is given at the end of this page, but you don’t need to read that to use the data files.
All the integers in the files are stored in the MSB first (high endian) format used by most non-Intel processors. Users of Intel processors and other low-endian machines must flip the bytes of the header.
在这里插入图片描述
在这里插入图片描述

接下来还要触发一个炸点就是magic number魔数
留一个链接,这个问题也会留在文末
https://www.zhihu.com/question/22018894

它的读取规则在一篇文章里面说的比较清楚
https://blog.csdn.net/qq_20936739/article/details/82011320

借用vscode的hexdum打开了这个二进制文件,先是对训练标签
这是开头
在这里插入图片描述
结尾
在这里插入图片描述
除去第一行offset,总共3750行每行16个,最后一行8个总共60008个。
第一行的前8位之后就是 标签的数据,这一点是可以通过变量监控验证的
依次是5、0、4、1、9、2、1、3
在这里插入图片描述
监控变量确实也是如此

另一个是mnist图片测试集
在这里插入图片描述

在这里插入图片描述
分析这个文件,490001行,每行8组,共3920008组,每组4位数。7840016组,每组两位。
按照我们的想法28pix的图像对应的像素点应该是784个,又有10000张图,则有7840000个组信息,那么这个文件多了16组,下面会慢慢说。

回到刚刚这个数据集
在这里插入图片描述
.
在这里插入图片描述
先说这个的实际效果,再说为什么会这样。
0000 0801 不读,读取是从offset的08位开始进行的,然后对应的是50419213,每一个偏置对应了一个标签值。
我们再看一下代码

labels = np.frombuffer(f.read(), np.uint8, offset=8)

代码中对offset的设定也是8,这里的8是指的8个字节。从第8个字节开始读取。
.
现在再回过头来分析为什么?
文档中指明了
offset的0000-0003是 magic number,所以跳过不读,
offset的0004-0007是items数目,0000EA60的十进制是10000,表示图片数量
接下来就是 05 00 04 01 09 02 01 03,这些代表的就是标签,对应数字5 0 4 1 9 1 3 而且不会超过9。
以上还有一个地方没有说到就是type。
0000-0007都是32 bit的整型数,在计算机中32位整型是二进制,用32个01来表达一个数,它可以表达的长度2^32
如果同样的范围用16进表达,只需要8位数字即可,16 ^8,比如上面的0000EA60,16进在32位整形下它表示的是一个数——10进的10000.
.
顺带一提这个bit的问题,一个字节是8bit,int32就是用4个字节来表达一个数,
当你用2进来查看的时候这个数是
32个数字0000 0000 0000 0000 0000 0000 0000 0001——(2 ^32),每8位一个字节
16进时,它是8个数字 0000 0001——(8 ^16),每2位一个字节
但是它们的字节数是一样的,也就是都占用了4个,不是8也不是32。
说到底字节决定于2进的长度,8位2进一个字节。16进不过是变化了一下,节约了描述长度。

同理,图片的描述
在这里插入图片描述

在这里插入图片描述

0000-0003——int32 取8位16进,…0000 0803,魔数,4个字节
0004-0007——int32 再取8位16进,0000 2710,图片数目,4个字节
0008-0011——int32 又是8位16进,0000 001c,图片的尺寸
0012-0015——int32 还是8位16进,0000 001c,图片在另一个方向上的尺寸
截止到现在,还没有读取实质内容。
截取一段代码

data = np.frombuffer(f.read(), np.uint8, offset=16)

和上面有所不同,就是offset的读取起点是第16个字节。
整个第一行都没有包含图片的实际信息
从第二行开始就有了,回到刚刚提到的7840016组信息,多出来的16就是第一行的信息,每2位16进是一个字节,一共多了16个字节,所以offset是16.

这里

2.numpy的frombuffer方法

  • numpy.frombuffer文档
    在这里插入图片描述

numpy的官方文档倒是说得很清楚,这里必须回到问题的原背景。我们可以顺藤摸瓜
注意到代码的这段

with gzip.open(file_path, 'rb') as f:
            data = np.frombuffer(f.read(), np.uint8, offset=16)

慢慢摸过去

  • numpy.frombuffer()方法

numpy对这个f.read()的解释是

buffer : buffer_like
An object that exposes the buffer interface.

所以,这个buffer是个什么东西?(是个流。)
所以要继续回到它的上一层,找到gzip下的f对象,f.read()方法返回的是什么

  • gzip.open()方法

gzip.open(filename, mode='rb', compresslevel=9, encoding=None, errors=None, newline=None)
Open a gzip-compressed file in binary or text mode, returning a file object.
这个前面说到过。打开二进制返回一个文件操作对象
对变量进行监控,得到
在这里插入图片描述
.

  • _io.BufferedReader

注意到对象的名称是_io.BufferedReader,继续摸下去
先说一下buffer

https://www.cnblogs.com/skywang12345/p/io_23.html
BufferReader的作用是为其它Reader提供缓冲功能。创建BufferReader时,我们会通过它的构造函数指定某个Reader为参数。BufferReader会将该Reader中的数据分批读取,每次读取一部分到缓冲中;操作完缓冲中的这部分数据之后,再从Reader中读取下一部分的数据。
为什么需要缓冲呢?原因很简单,效率问题!缓冲中的数据实际上是保存在内存中,而原始数据可能是保存在硬盘或NandFlash中;而我们知道,从内存中读取数据的速度比从硬盘读取数据的速度至少快10倍以上。
那干嘛不干脆一次性将全部数据都读取到缓冲中呢?读取全部的数据所需要的时间可能会很长。

到目前为止基本搞清楚了,buffer是用来干什么的。
.
下面是关于io 的文档

https://docs.python.org/3.6/library/io.html

.gzip返回的就是一个f对象,我们可以查看它的内容就是_io.BufferedReader
f对象的read()方法得到的是b'\x0\x0'这样的16进,类型是bytes
这个文章里面谈到了java的 https://www.cnblogs.com/ysocean/p/6870069.html
io.bufferedReader是在gzip的时候就完成了反序列化,转化成了对象
而后在read方法又进行了序列化

序列化:把对象转换为字节序列的过程称为对象的序列化。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

序列化的意义在于让信息长久地保存下来,将内存中的对象变成字节描述。这里似乎没有用这个技能,提一下,3章是用的np按照字节解析之后,用pickle把结果以字典的形式保存下来了,字典里面是np对象,可操作的数组。

相关: https://blog.csdn.net/qq_27093465/article/details/78544505

继续深入下去,

https://docs.python.org/3.6/library/io.html#io.BufferedReader
在这里插入图片描述
这个read返回的就是字节流

这里触发RawIOBaseBufferedIOBaseDEFAULT_BUFFER_SIZE,可以自行去看看

先说IO的问题

简单点就是输入输出
在这里插入图片描述
参考:https://www.jb51.net/article/123768.htm
从对文件的操作来讲,有读和写的操作——也就是输入和输出。
从流的流向来讲,有输入和输出之分。
从流的内容来讲,有字节和字符之分。

后来我手动测试过
在这里插入图片描述
只要传入字节流就能正确输出,不管里面是8进还是16进
官方给的例子中也是如此。

.
.

补充

1.b字节流问题,字节序列
2.魔数问题
3.cache和buffer
4.int8这类问题,有无符号位
序列化和反序列化 buffer协议

后记:
读别人的代码就是容易卡死(脑子卡死)
才发现自己写得多么low,在不同文件里面跳转,复用,里面的技巧,太多了。
讲真,还算常用numpy(其实也只是用了点基本的)结果连数据类型的概念都没有,猛然想起mayavi里面以前有过一个'>i2'的数据类型,一下恐慌了起来。当时并没有引起注意,等到别人问起我的时候,一脸懵逼。
所以啊,年轻人啊,学东西得踏踏实实。
然后这篇就是从numpy的frombuffer扩展到numpy的数据类型。
numpy文档的exp报错,导致我得去stackflow和GitHub,不能太依赖中文资料,反馈是可能会导致进入不适区。

相关的资料:

详解IDX-Ubyte文件格式 及 python读取 https://blog.csdn.net/qq_20936739/article/details/82011320(这篇比较推荐)
手写数据集的原网址 http://yann.lecun.com/exdb/mnist/
二进制数据流 https://blog.csdn.net/changwilling/article/details/52065955
IO讲解 https://www.jb51.net/article/123768.htm
文件流 https://blog.csdn.net/qiaojialin/article/details/81031422

MNIST数据库介绍及转换 https://blog.csdn.net/fengbingchun/article/details/49611549
MNIST数据集格式ubyte转png https://blog.csdn.net/haoji007/article/details/76998140
使用Python解析MNIST数据集(IDX文件格式)https://www.jianshu.com/p/84f72791806f
python读取mnist https://www.cnblogs.com/x1957/archive/2012/06/02/2531503.html
Python中对字节流/二进制流的操作:struct模块简易使用教程 https://www.jianshu.com/p/5a985f29fa81

2018.10.31.——大体写完,实际上很多地方没展开。

  • 36
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Fashion-MNIST数据集是一个用于图像分类任务的数据集,包含了10个类别的70,000张28x28的灰度图像。下面是下载和读取Fashion-MNIST数据集的示例代码: 下载数据集: ```python import urllib.request import os url_train = 'http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz' url_train_label = 'http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz' url_test = 'http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz' url_test_label = 'http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz' os.makedirs('./data/fashion_mnist', exist_ok=True) urllib.request.urlretrieve(url_train, './data/fashion_mnist/train-images-idx3-ubyte.gz') urllib.request.urlretrieve(url_train_label, './data/fashion_mnist/train-labels-idx1-ubyte.gz') urllib.request.urlretrieve(url_test, './data/fashion_mnist/t10k-images-idx3-ubyte.gz') urllib.request.urlretrieve(url_test_label, './data/fashion_mnist/t10k-labels-idx1-ubyte.gz') ``` 读取数据集: ```python import gzip import numpy as np def load_mnist_images(filename): with gzip.open(filename, 'rb') as f: data = np.frombuffer(f.read(), np.uint8, offset=16) return data.reshape(-1, 28, 28) def load_mnist_labels(filename): with gzip.open(filename, 'rb') as f: data = np.frombuffer(f.read(), np.uint8, offset=8) return data train_images = load_mnist_images('./data/fashion_mnist/train-images-idx3-ubyte.gz') train_labels = load_mnist_labels('./data/fashion_mnist/train-labels-idx1-ubyte.gz') test_images = load_mnist_images('./data/fashion_mnist/t10k-images-idx3-ubyte.gz') test_labels = load_mnist_labels('./data/fashion_mnist/t10k-labels-idx1-ubyte.gz') ``` 这里的`load_mnist_images`和`load_mnist_labels`函数用于读取数据集文件,并将其转换为NumPy数组。`train_images`和`test_images`是形状为`(60000, 28, 28)`和`(10000, 28, 28)`的数组,表示训练集和测试集的图像数据,每张图像的大小为28x28。`train_labels`和`test_labels`是形状为`(60000,)`和`(10000,)`的数组,表示训练集和测试集的标签数据。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值