在開始之前,首先聲明本篇文章參考官方實現一個圖片分類應用,我基於官網的這篇文章加以自己的理解發表了這篇博客,希望大家能夠更快更簡單直觀的體驗MindSpore,如有不妥的地方歡迎大家指正。
【本文代碼編譯環境為MindSpore1.3.0 CPU版本】
准備環節
- 確保已安裝MindSpore(可以根據自己的硬件情況安裝,CPU,GPU,Ascend環境均可)
- 選擇一個集成開發工具(Jupyter Notebook,Pycharm等),我選擇的是Pycharm
- 查看是否安裝python中的畫圖庫 matplotlib,若未安裝,在終端輸入pip install matplotlib
數據集
下載數據集
手寫數字識別初體驗采用的是業界經典的MNIST數據集,它包含了60000張訓練圖片,10000張測試圖片。
MindSpore中提供了很多經典數據集的下載命令,其中自然包括MNIST數據集。在Jupyter Notebook中執行如下命令下載MNIST數據集。
Copy!mkdir -p ./datasets/MNIST_Data/train ./datasets/MNIST_Data/test
!wget -NP ./datasets/MNIST_Data/train https://mindspore-website.obs.myhuaweicloud.com/notebook/datasets/mnist/train-labels-idx1-ubyte --no-check-certificate
!wget -NP ./datasets/MNIST_Data/train https://mindspore-website.obs.myhuaweicloud.com/notebook/datasets/mnist/train-images-idx3-ubyte --no-check-certificate
!wget -NP ./datasets/MNIST_Data/test https://mindspore-website.obs.myhuaweicloud.com/notebook/datasets/mnist/t10k-labels-idx1-ubyte --no-check-certificate
!wget -NP ./datasets/MNIST_Data/test https://mindspore-website.obs.myhuaweicloud.com/notebook/datasets/mnist/t10k-images-idx3-ubyte --no-check-certificate
!tree ./datasets/MNIST_Data
得到的文檔目錄結構如圖所示:
./datasets/MNIST_Data
├── test
│ ├── t10k-images-idx3-ubyte
│ └── t10k-labels-idx1-ubyte
└── train
├── train-images-idx3-ubyte
└── train-labels-idx1-ubyte
2 directories, 4 files
./ 表示當前目錄的意思,(當前項目中有一個datasets的文件夾,datasets文件夾下有一個MNIST_Data文件夾,然后依次類推)。
這里我沒有采用命令下載的方法,因為我用的是pycharm。筆者可前往http://yann.lecun.com/exdb/mnist/下載MNIST數據集,並將它按照文檔目錄結構圖的形式放置於我們的項目中。
配置運行信息
接着我們配置MindSpore的運行模式,硬件信息等。
from mindspore import context context.set_context(mode=context.GRAPH_MODE, device_target="CPU")
導入context模塊,調用context的set_context方法,參數mode表示運行模式,MindSpore支持動態圖和靜態圖兩種模式,GRAPH_MODE表示靜態圖模式,PYNATIVE_MODE表示動態圖模式,對於手寫數字識別問題來說采用靜態圖模式即可。參數device_target表示硬件信息,有三個選項(CPU,GPU,Ascend),讀者可根據自己的真實環境選擇。
查看數據集的詳細信息
既然我們已經將MNIST數據集下載到本地,那我們自然得去看一下它里面到底有什么東西,是否真的是我們熟知的手寫阿拉伯數字。這里我們就要采用畫圖庫了。
import matplotlib.pyplot as plt import matplotlib import numpy as np import mindspore.dataset as ds train_data_path = "./datasets/MNIST_Data/train" test_data_path = "./datasets/MNIST_Data/test" mnist_ds = ds.MnistDataset(train_data_path) #生成的圖像有兩列[img, label],列圖像張量是uint8類型, #因為像素點的取值范圍是(0, 255),需要注意的是這兩列數據的數據類型是Tensor print('The type of mnist_ds:', type(mnist_ds)) print("Number of pictures contained in the mnist_ds:", mnist_ds.get_dataset_size()) #int, number of batches. #print(mnist_ds.get_batch_size()) #Return the size of batch. dic_ds = mnist_ds.create_dict_iterator() #數據集上創建迭代器,為字典數據類型,輸出的為Tensor類型 item = next(dic_ds) img = item["image"].asnumpy() #asnumpy為Tensor中的方法,功能是將張量轉化為numpy數組,因為matplotlib.pyplot中不 #能接受Tensor數據類型的參數 label = item["label"].asnumpy() print("The item of mnist_ds:", item.keys()) print("Tensor of image in item:", img.shape) print("The label of item:", label) plt.imshow(np.squeeze(img)) #squeeze將shape中為1的維度去掉,plt.imshow()函數負責對圖像進行處理,並顯示其格式 plt.title("number:%s"% item["label"].asnumpy()) plt.show() #show顯示圖像
代碼部分的解釋,我想注釋已經寫的很明白了,但我們需要注意幾點。
- 我們盡量將自己拿到的數據集轉化為MindSpore能夠處理的數據類型,因為框架里面加了一些對於數據加速的方法。當然,這里的MNIST數據集不需要我們使用復雜的加載,因為對於MNIST這一類經典的數據集MindSpore都提供了專門的加載方法,將它加載成MindSpore能處理的數據。
- MindSpore提供的內置數據集處理方法默認輸出一般都是在框架中通用的Tensor,但是對於非框架優化包含的python庫,包括matplotlib,它們就無法處理接受Tensor,這是我們就要采用Tensor類中定義的asnumpy方法將張量轉化為numpy數組
上述代碼的運行截圖:
上述結果驗證了我們前面的說法,訓練集中有60000張圖片,以image和lable兩個標簽分別儲存,並且每一張圖片的大小為28 x 28,通道數為1說明里面是黑白圖片。圖中顯示的圖片是6(我覺得有點不像),它的標簽值也為6。
加載及處理數據集
定義一個create_dataset函數。在這個函數中,總體分為一下幾步。
- 將原始的MNIST數據集加載進來, mnist_ds = ds.MnistDataset(data_path)
- 設置一些數據增強和處理所需要的參數(parameters)
- 使用2中設置的參數得到一些實例化的數據增強和處理方法
- 將3中的方法映射到(使用)在對應數據集的相應部分(image,label)
- 返回處理好的數據集(不同的神經網絡可以接受不同的輸入數據格式)
create_dataset函數代碼如下:
import mindspore.dataset.vision.c_transforms as CV import mindspore.dataset.transforms.c_transforms as C from mindspore.dataset.vision import Inter from mindspore import dtype as mstype def create_dataset(data_path, batch_size=32, repeat_size=1, num_parallel_workers=1): mnist_ds = ds.MnistDataset(data_path) # 定義數據增強和處理所需的一些參數 resize_height, resize_width = 32, 32 rescale = 1.0 / 255.0 shift = 0.0 rescale_nml = 1 / 0.3081 shift_nml = -1 * 0.1307 / 0.3081 # 根據上面所定義的參數生成對應的數據增強方法,即實例化對象 resize_op = CV.Resize((resize_height, resize_width), interpolation=Inter.LINEAR) rescale_nml_op = CV.Rescale(rescale_nml, shift_nml) rescale_op = CV.Rescale(rescale, shift) hwc2chw_op = CV.HWC2CHW() type_cast_op = C.TypeCast(mstype.int32) # 將數據增強處理方法映射到(使用)在對應數據集的相應部分(image,label) mnist_ds = mnist_ds.map(operations=type_cast_op, input_columns="label", num_parallel_workers=num_parallel_workers) mnist_ds = mnist_ds.map(operations=resize_op, input_columns="image", num_parallel_workers=num_parallel_workers) mnist_ds = mnist_ds.map(operations=rescale_op, input_columns="image", num_parallel_workers=num_parallel_workers) mnist_ds = mnist_ds.map(operations=rescale_nml_op, input_columns="image", num_parallel_workers=num_parallel_workers) mnist_ds = mnist_ds.map(operations=hwc2chw_op, input_columns="image", num_parallel_workers=num_parallel_workers) # 處理生成的數據集 buffer_size = 10000 mnist_ds = mnist_ds.shuffle(buffer_size=buffer_size) mnist_ds = mnist_ds.batch(batch_size, drop_remainder=True) mnist_ds = mnist_ds.repeat(repeat_size) return mnist_ds
導入的模塊:
- mindspore.dataset.vision.c_transforms:MindSpore提供的通過數據增強操作對圖像進行預處理,從而提高模型的廣泛性。它是基於基於C++的OpenCV實現,具有較高的性能。接着來看一下,在增強MNIST數據集中,我們所使用到該模塊中的方法(算子)。
CV.Resize(size, interpolation=Inter.LINEAR):
功能:使用給定的插值模式將輸入圖像調整為給定大小。
參數:size:調整后輸出圖像的大小;interpolation:圖像的插值模式(5個常量可供選擇),一般情況下使用默認的Inter.LINEAR即可。
CV.Rescale(rescale, shift):
功能:使用給定的重縮放和移位重縮放輸入圖像。此運算符將使用以下命令重新縮放輸入圖像:輸出=圖像重新縮放+移位(output = image rescale + shift),可以用來消除圖片在不同位置給模型帶來的影響。
參數:rescale:縮放因子;shift:偏移因子
CV.HWC2CHW():
功能:將輸入圖像從形狀(H,W,C)轉換為形狀(C,H,W)。輸入圖像應為3通道圖像。
- mindspore.dataset.transforms.c_transforms:該模塊也是一個用於支持常見的圖形增強功能的模塊,基於C++的OpenCV實現的。
C.TypeCast(data_type):
功能:一個轉化為給定MindSpore數據類型(data_type)的張量操作。
參數:data_type:MindSpore中dtype模塊中所指定的數據類型。
- mindspore.dataset.MnistDataset,MindSpore中專門提供的加載處理MNIST數據集為MindSpore數據類型的類,這里主要看一下它的map方法。
ds.map
(operations, input_columns=None, output_columns=None, column_order=None, num_parallel_workers=None, python_multiprocessing=False, cache=None, callbacks=None):
功能:將具體的操作和方法應用到數據集上
參數:我們主要看一下使用到的參數,因為它的很多參數實際上是默認的。operations:用於數據集的操作列表,操作將按照他們在列表中的順序進行應用。 ;input_columns:將作為輸入傳遞給第一個操作的列的名稱列表。 ;num_parallel_workers:用於並行處理數據集的線程數,默認值為無。
參數含義
現在既然我們清楚了這些方法的功能以及他們中參數的含義,那么這里為什么要將參數定義成這樣呢?
resize_height, resize_width = 32, 32
resize_op = CV.Resize((resize_height, resize_width), interpolation=Inter.LINEAR)
mnist_ds = mnist_ds.map(operations=resize_op, input_columns="image", num_parallel_workers=num_parallel_workers)
我們要將MNIST數據中的28 x 28大小的圖片轉化為32 x 32的大小,這是因為后面我們要采用的LeNet-5的卷積神經網絡訓練模型,該模型要求輸入圖像的尺寸統一歸一化為32 x 32,具體的關於該網絡的細節,我會在構建網絡時詳細介紹。
rescale = 1.0 / 255.0, shift = 0.0
rescale_op = CV.Rescale(rescale, shift)
mnist_ds = mnist_ds.map(operations=rescale_op, input_columns="image", num_parallel_workers=num_parallel_workers)
這里我們將數據集中圖像進行縮放,選擇除以255是因為像素點的取值范圍是(0,255)。該操作可以使得每個像素的數值大小在(0,1)范圍中,可以提升訓練效率。進行完該操作之后,有對圖像進行了依次縮放,不過那個縮放的值應該是總結出來的能夠進一步提高訓練效率的值,需要大量實驗才能得到,這里我直接是使用了官方代碼上的值。
hwc2chw_op = CV.HWC2CHW()
使用它對圖片張量進行轉化的原因是原圖片張量形狀是(28 x 28 x 1)(高 x 寬 x通道),在python中用高維數組解釋是28個 28 x 1的矩陣,這不是很符合我們的計算習慣,所以我們將它轉化為1 x 28 x 28(通道 x 高 x 寬),相對於是1個 28 x 28的矩陣,這不就是我們推導計算過程中常用的值嗎。因此,它能夠方便我們進行數據訓練。
buffer_size = 10000
mnist_ds = mnist_ds.shuffle(buffer_size=buffer_size)
在該操作之前,我們已經將MNIST數據集處理成網絡和框架所能接受的數據。接着就是對處理好的數據集進行一個增強操作。
shuffle用於將數據集隨機打亂,我們不希望有太多的人為因素干擾我們的訓練,因為可能MNIST數據集本身是有一定規律的,我們不希望按照制作這個數據集的前輩的順序來訓練神經網絡。
mnist_ds = mnist_ds.batch(batch_size, drop_remainder=True)
將整個數據集按照batch_size的大小分為若干批次,每一次訓練的時候都是按一個批次的數據進行訓練, drop_remainder:確定是否刪除數據行數小於批大小的最后一個塊,這里設置為True就是只保留數據集個數整除batch_size后得到的批次,多余得則從minst_ds中舍棄。
mnist_ds = mnist_ds.repeat(repeat_size)
將數據集重復repeat_size次,需要注意得是該操作一般使用在batch操作之后。
原理講完之后,讓我們看一下處理好的數據集是什么樣子的吧。
數據集的展示
ms_dataset = create_dataset(train_data_path)
print('Number of each group',ms_dataset.)
print('Number of groups in the dataset:', ms_dataset.get_dataset_size())
可以看到,我們將32張圖片作為一個批次,將訓練集總共分為了60000/32 = 1875個批次。接着我們拿出一個批次的數據將其形狀等屬性顯示出來並將它進行可視化。
data = next(ms_dataset.create_dict_iterator(output_numpy=True)) images = data["image"] labels = data["label"] print('Tensor of image:', images.shape) print('Labels:', labels) count = 1 for i in images: plt.subplot(4, 8, count) plt.imshow(np.squeeze(i)) plt.title('num:%s'%labels[count-1]) plt.xticks([]) count += 1 plt.axis("off") plt.show()
從Tensor of image:可以看出我們在處理數據的操作中執行Resize和HWC2CHW成功將圖片轉化為1 x 32 x 32的,同時可以知道所謂的分批次就是將batch_size張圖片放在一起,用矩陣表示就是將batch_size個矩陣合成一個大型的矩陣,每一次使用一個這樣的大型矩陣去訓練,一個一個來。這樣可以避免過高維度數組造成的內存過大,訓練時間過長的問題,接着看一下圖片顯示。
本篇文章講到主要是手寫數字識別初體驗的准備以及數據集的加載與處理環節,希望大家能有所收獲。在下一篇文章中,我將給大家介紹卷積神經網絡LeNet-5的一些基礎知識,以及如何使用MindSpore來構建出一個LeNet-5網絡。