順藤摸瓜-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.——大體寫完,實際上很多地方沒展開。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章