[python][機(jī)器學(xué)習(xí)][scipy]使用稀疏矩陣實現(xiàn)卷積運(yùn)算

最后一次更新日期: 2019/3/21

此篇文章提供一個將scipy庫的稀疏矩陣運(yùn)用于卷積運(yùn)算上,以期在不增加過多內(nèi)存開銷的情況下提高性能的入門思路。

先導(dǎo)入以下模塊:
import numpy as np
from scipy import sparse
import time

1.基于滑窗的卷積運(yùn)算

卷積核

卷積核的三個軸分別對應(yīng):高,寬,顏色通道

In [5]: kernel=np.eye(3).repeat(3).reshape((3,3,3))

In [6]: kernel[:,:,0]
Out[6]: 
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

圖片數(shù)據(jù)

cifar10數(shù)據(jù)集作為示例,加載數(shù)據(jù)集的方法此處不作講解。
數(shù)據(jù)集的四個軸分別對應(yīng):樣本,高,寬,顏色通道

In [11]: cifar=CifarManager()
    ...: train_images,train_labels,test_images,test_labels=cifar.read_as_array(chinese_label=True)
    ...: The default path of image data is set to:
    ...: D:\training_data\used\cifar-10-batches-py
    ...: reading data---
    ...: completed

In [12]: test_images.shape
Out[12]: (10000, 32, 32, 3)

In [13]: Image.fromarray(test_images[0].astype('uint8')).resize((200,200))
Out[13]: 

圖片數(shù)據(jù)集的數(shù)據(jù)結(jié)構(gòu)如下圖所示:

axis0對應(yīng)樣本,axis1對應(yīng)圖片的高,axis2對應(yīng)圖片的寬,axis3對應(yīng)顏色通道。
從中抽取一張圖片,則axis0,axis1,axis2分別對應(yīng)高,寬,通道

單圖卷積運(yùn)算

#array: 單張圖片數(shù)據(jù)
#kernel: 卷積核
#step: 步長
def conv1(array,kernel,step=1):
    #圖片的高和寬
    h,w=array.shape[:2]
    #卷積核的高和寬
    kh,kw=kernel.shape[:2]
    #縱向和橫向的移動步數(shù)
    hs,ws=(h-kh)//step+1,(w-kw)//step+1
    #初始化輸出數(shù)組
    out=np.empty((hs,ws))
    #縱向滑動卷積核
    for i in range(hs):
        #卷積核的縱向覆蓋范圍
        start0,end0=i*step,i*step+kh
        #橫向滑動卷積核
        for j in range(ws):
            #卷積核的橫向覆蓋范圍
            start1,end1=j*step,j*step+kw
            #該位置的卷積運(yùn)算
            out[i,j]=np.vdot(array[start0:end0,start1:end1],kernel)
    return out

In [37]: out=conv1(test_images[0],kernel)

In [38]: out.shape
Out[38]: (30, 30)

In [39]: out_=(out-out.min())/(out.max()-out.min())*255
    ...: Image.fromarray(out_.astype('uint8')).resize((200,200))
Out[39]: 

np.vdot用于將數(shù)組展開為向量后進(jìn)行點積運(yùn)算。

卷積運(yùn)算最為簡單的實現(xiàn)方式是滑窗,用卷積核在每個位置上覆蓋圖像數(shù)據(jù),對應(yīng)元素相乘再求和,存入結(jié)果數(shù)組的相對位置。

單張圖片,單個卷積核,在某一個位置的卷積運(yùn)算如下圖所示:

批量卷積運(yùn)算

#array: 圖片數(shù)據(jù)集
#kernel: 卷積核
#step: 步長
def conv2(array,kernel,step=1):
    #圖片的張數(shù)、高、寬
    n,h,w=array.shape[:3]
    #卷積核的高、寬、顏色通道
    kh,kw,kc=kernel.shape[:3]
    #縱向和橫向的移動步數(shù)
    hs,ws=(h-kh)//step+1,(w-kw)//step+1
    #初始化輸出數(shù)組
    out=np.empty((n,hs,ws))
    #縱向滑動卷積核
    for i in range(hs):
        #卷積核的縱向覆蓋范圍
        start0,end0=i*step,i*step+kh
        #橫向滑動卷積核
        for j in range(ws):
            #卷積核的橫向覆蓋范圍
            start1,end1=j*step,j*step+kw
            #該位置的卷積運(yùn)算
            array_=array[:,start0:end0,start1:end1].reshape((-1,kh*kw*kc))
            kernel_=kernel.ravel()
            out[:,i,j]=np.dot(array_,kernel_)
    return out

In [50]: start=time.clock()
    ...: out2=conv2(test_images,kernel)
    ...: print('\ntime used: %f s'%(time.clock()-start))

time used: 2.255798 s

In [51]: out2.shape
Out[51]: (10000, 30, 30)

In [52]: (out==out2[0]).all()
Out[52]: True

np.dot可以用于完成 向量點積,矩陣與向量的乘積,矩陣乘法 等運(yùn)算。

在進(jìn)行某一位置的卷積運(yùn)算時,先在數(shù)據(jù)集上提取卷積核覆蓋范圍的數(shù)據(jù),如上方的例子,可以得到一個形狀為(10000,3,3,3)的張量,第一個軸對應(yīng)每張圖片,后三個軸對應(yīng)卷積核的三個軸。將子數(shù)據(jù)集的后三個軸展開,形狀變?yōu)?code>(10000,27),卷積核展開為向量,形狀變?yōu)?code>(27,),然后進(jìn)行一次矩陣和列向量的乘積運(yùn)算就得到卷積結(jié)果了。

(為方便描述,公式中皆以1為起始索引)
以位置1,1為例,n,h,w,i,j分別對應(yīng)樣本數(shù),圖片的高,圖片的寬,縱向滑動的步數(shù),橫向滑動的步數(shù),計算的主要過程如下:

(1).第一張圖片的數(shù)據(jù):(\left[ \begin{matrix} a_{111} & a_{121} & \cdots & a_{1w1}\\ a_{211} & a_{221} & \cdots & a_{2w1}\\ \vdots & \vdots & \ddots & \vdots\\ a_{h11} & a_{h21} & \cdots & a_{hw1}\\ \end{matrix} \right], \left[ \begin{matrix} a_{112} & a_{122} & \cdots & a_{1w2}\\ a_{212} & a_{222} & \cdots & a_{2w2}\\ \vdots & \vdots & \ddots & \vdots\\ a_{h12} & a_{h22} & \cdots & a_{hw2}\\ \end{matrix} \right], \left[ \begin{matrix} a_{113} & a_{123} & \cdots & a_{1w3}\\ a_{213} & a_{223} & \cdots & a_{2w3}\\ \vdots & \vdots & \ddots & \vdots\\ a_{h13} & a_{h23} & \cdots & a_{hw3}\\ \end{matrix} \right])
\Rightarrow 單張圖片被卷積核覆蓋的局部:( \left[ \begin{matrix} a_{111} & a_{121} & a_{131} \\ a_{211} & a_{221} & a_{231} \\ a_{311} & a_{321} & a_{331} \\ \end{matrix} \right], \left[ \begin{matrix} a_{112} & a_{122} & a_{132} \\ a_{212} & a_{222} & a_{232} \\ a_{312} & a_{322} & a_{332} \\ \end{matrix} \right], \left[ \begin{matrix} a_{113} & a_{123} & a_{133} \\ a_{213} & a_{223} & a_{233} \\ a_{313} & a_{323} & a_{333} \\ \end{matrix} \right])
\Rightarrow 局部圖片轉(zhuǎn)換為向量: \left[ \begin{matrix} a_{111}\\ a_{112}\\ a_{113}\\ a_{121}\\ \vdots\\ a_{333}\\ \end{matrix} \right] \Rightarrow所有圖片行向量組成矩陣: \left[ \begin{matrix} a_{1111} & a_{1112} & a_{1113} & a_{1121} & \cdots & a_{1333}\\ a_{2111} & a_{2112} & a_{2113} & a_{2121} & \cdots & a_{2333}\\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots\\ a_{n111} & a_{n112} & a_{n113} & a_{n121} & \cdots & a_{n333}\\\\ \end{matrix} \right]
(2). 卷積核:( \left[ \begin{matrix} k_{111} & k_{121} & k_{131}\\ k_{211} & k_{221} & k_{231}\\ k_{311} & k_{321} & k_{331}\\ \end{matrix} \right], \left[ \begin{matrix} k_{112} & k_{122} & k_{132}\\ k_{212} & k_{222} & k_{232}\\ k_{312} & k_{322} & k_{332}\\ \end{matrix} \right], \left[ \begin{matrix} k_{113} & k_{123} & k_{133}\\ k_{213} & k_{223} & k_{233}\\ k_{313} & k_{323} & k_{333}\\ \end{matrix} \right]) \Rightarrow \left[ \begin{matrix} k_{111}\\ k_{112}\\ k_{113}\\ k_{121}\\ \vdots\\ k_{333}\\ \end{matrix} \right]
(3). (1,1)位置的輸出: \left[ \begin{matrix} a_{1111} & a_{1112} & a_{1113} & a_{1121} & \cdots & a_{1333}\\ a_{2111} & a_{2112} & a_{2113} & a_{2121} & \cdots & a_{2333}\\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots\\ a_{n111} & a_{n112} & a_{n113} & a_{n121} & \cdots & a_{n333}\\\\ \end{matrix} \right] \cdot \left[ \begin{matrix} k_{111}\\ k_{112}\\ k_{113}\\ k_{121}\\ \vdots\\ k_{333}\\ \end{matrix} \right]= \left[ \begin{matrix} out_{111}\\ out_{211}\\ \vdots\\ out_{n11}\\ \end{matrix} \right]
(4). 結(jié)果的整合:( \left[ \begin{matrix} out_{111} & out_{112} & \cdots & out_{11j}\\ out_{121} & out_{122} & \cdots & out_{12j}\\ \vdots & \vdots & \ddots & \vdots\\ out_{1i1} & out_{1i2} & \cdots & out_{1ij}\\ \end{matrix} \right], \cdots, \left[ \begin{matrix} out_{n11} & out_{n12} & \cdots & out_{n1j}\\ out_{n21} & out_{n22} & \cdots & out_{n2j}\\ \vdots & \vdots & \ddots & \vdots\\ out_{ni1} & out_{ni2} & \cdots & out_{nij}\\ \end{matrix} \right])

2.基于權(quán)重矩陣的卷積運(yùn)算

def conv3(array,kernel,step=1):
    #圖片的張數(shù)、高、寬
    n,h,w=array.shape[:3]
    #卷積核的高、寬、顏色通道
    kh,kw,kc=kernel.shape[:3]
    #縱向和橫向的移動步數(shù)
    hs,ws=(h-kh)//step+1,(w-kw)//step+1
    #滑窗卷積運(yùn)算等效的權(quán)重矩陣
    weights=np.zeros((h*w*3,hs*ws))
    #縱向位置變化
    for i in range(hs):
        #卷積核的縱向覆蓋范圍
        start0,end0=i*step,i*step+kh
        #橫向位置變化
        for j in range(ws):
            #卷積核的橫向覆蓋范圍
            start1,end1=j*step,j*step+kw
            #當(dāng)前位置的權(quán)重向量
            weights_=np.zeros((h,w,3))
            weights_[start0:end0,start1:end1]=kernel
            #合并至矩陣
            weights[:,i*ws+j]=weights_.ravel()
    #圖片數(shù)據(jù)集變形
    array_=array.reshape((n,h*w*3))
    #矩陣乘法計算卷積結(jié)果
    result=np.dot(array_,weights)
    return result.reshape((-1,hs,ws)),weights

In [18]: start=time.clock()
    ...: out3,weights3=conv3(test_images,kernel)
    ...: print('\ntime used: %f s'%(time.clock()-start))

time used: 0.560485 s

In [19]: (out2==out3).all()
Out[19]: True

In [20]: weights.shape
Out[20]: (3072, 900)

In [21]: weights3.size
Out[21]: 2764800

可以看到,在消去循環(huán)體,將原本基于滑窗的卷積運(yùn)算轉(zhuǎn)換為權(quán)重矩陣運(yùn)算后,性能得到了明顯的提升(約3倍)。
但隨之而來的問題就是,構(gòu)造權(quán)重矩陣空間開銷過大,權(quán)重矩陣形狀為(輸入總大小,輸出總大小),即使在低分辨率的cifar數(shù)據(jù)集上,該矩陣也達(dá)到了2764800個元素的大小,隨著圖片分辨率的提高,開銷會增大到無法接受,乃至有性能倒退的可能。
考慮到卷積運(yùn)算的權(quán)重矩陣有大量的零元素,這些元素對計算沒有貢獻(xiàn),應(yīng)當(dāng)采用稀疏矩陣的形式進(jìn)行處理。

對基于滑窗的計算方式進(jìn)行轉(zhuǎn)換的思路,以起始位置為例:
(1). 卷積核:( \left[ \begin{matrix} k_{111} & k_{121} & k_{131}\\ k_{211} & k_{221} & k_{231}\\ k_{311} & k_{321} & k_{331}\\ \end{matrix} \right], \left[ \begin{matrix} k_{112} & k_{122} & k_{132}\\ k_{212} & k_{222} & k_{232}\\ k_{312} & k_{322} & k_{332}\\ \end{matrix} \right], \left[ \begin{matrix} k_{113} & k_{123} & k_{133}\\ k_{213} & k_{223} & k_{233}\\ k_{313} & k_{323} & k_{333}\\ \end{matrix} \right] )
\Rightarrow 結(jié)合位置拓展為權(quán)重矩陣:( \left[ \begin{matrix} k_{111} & k_{121} & k_{131} & \cdots & 0\\ k_{211} & k_{221} & k_{231} & \cdots & 0\\ k_{311} & k_{321} & k_{331} & \cdots & 0\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ 0 & 0 & 0 & \cdots & 0\\ \end{matrix} \right], \left[ \begin{matrix} k_{112} & k_{122} & k_{132} & \cdots & 0\\ k_{212} & k_{222} & k_{232} & \cdots & 0\\ k_{312} & k_{322} & k_{332} & \cdots & 0\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ 0 & 0 & 0 & \cdots & 0\\ \end{matrix} \right], \left[ \begin{matrix} k_{113} & k_{123} & k_{133} & \cdots & 0\\ k_{213} & k_{223} & k_{233} & \cdots & 0\\ k_{313} & k_{323} & k_{333} & \cdots & 0\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ 0 & 0 & 0 & \cdots & 0\\ \end{matrix} \right] )
\Rightarrow 展開為向量: \left[ \begin{matrix} k_{111} \\ k_{112} \\ k_{113} \\ k_{121} \\ \vdots \\ 0 \\ \end{matrix} \right]\Rightarrow 所有位置(i,j)的權(quán)重列向量堆疊: \left[ \begin{matrix} k_{11111} & 0 & 0 & \cdots & 0 \\ k_{11112} & k_{12111} & 0 & \cdots & 0 \\ k_{11113} & k_{12112} & k_{13111} & \cdots & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & 0 & \cdots & k_{ij333} \\ \end{matrix} \right]
(2). 第一張圖片的數(shù)據(jù):( \left[ \begin{matrix} a_{111} & a_{121} & \cdots & a_{1w1}\\ a_{211} & a_{221} & \cdots & a_{2w1}\\ \vdots & \vdots & \ddots & \vdots\\ a_{h11} & a_{h21} & \cdots & a_{hw1}\\ \end{matrix} \right] , \left[ \begin{matrix} a_{112} & a_{122} & \cdots & a_{1w2}\\ a_{212} & a_{222} & \cdots & a_{2w2}\\ \vdots & \vdots & \ddots & \vdots\\ a_{h12} & a_{h22} & \cdots & a_{hw2}\\ \end{matrix} \right], \left[ \begin{matrix} a_{113} & a_{123} & \cdots & a_{1w3}\\ a_{213} & a_{223} & \cdots & a_{2w3}\\ \vdots & \vdots & \ddots & \vdots\\ a_{h13} & a_{h23} & \cdots & a_{hw3}\\ \end{matrix} \right])
\Rightarrow 完整圖片展開: \left[ \begin{matrix} a_{111}\\ a_{112}\\ a_{113}\\ a_{121}\\ \vdots\\ a_{hw3}\\ \end{matrix} \right] \Rightarrow 所有圖片行向量堆疊: \left[ \begin{matrix} a_{1111}&a_{1112}&a_{1113}&a_{1121}&\cdots&a_{1hw3}\\ a_{2111}&a_{2112}&a_{2113}&a_{2121}&\cdots&a_{2hw3}\\ \vdots & \vdots & \vdots& \vdots & \ddots &\vdots \\ a_{n111}&a_{n112}&a_{n113}&a_{n121}&\cdots&a_{nhw3}\\ \end{matrix} \right]
(3). 執(zhí)行矩陣乘法: \left[ \begin{matrix} a_{1111}&a_{1112}&a_{1113}&a_{1121}&\cdots&a_{1hw3}\\ a_{2111}&a_{2112}&a_{2113}&a_{2121}&\cdots&a_{2hw3}\\ \vdots & \vdots & \vdots& \vdots & \ddots &\vdots \\ a_{n111}&a_{n112}&a_{n113}&a_{n121}&\cdots&a_{nhw3}\\ \end{matrix} \right] \cdot \left[ \begin{matrix} k_{11111} & 0 & 0 & \cdots & 0 \\ k_{11112} & k_{12111} & 0 & \cdots & 0 \\ k_{11113} & k_{12112} & k_{13111} & \cdots & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & 0 & \cdots & k_{ij333} \\ \end{matrix} \right]
= \left[ \begin{matrix} out_{111} & out_{112} & out_{113} & \cdots & out_{1ij}\\ out_{211} & out_{212} & out_{213} & \cdots & out_{2ij}\\ out_{311} & out_{312} & out_{313} & \cdots & out_{3ij}\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ out_{n11} & out_{n12} & out_{n13} & \cdots & out_{nij}\\ \end{matrix} \right] \Rightarrow 形狀還原:( \left[ \begin{matrix} out_{111} & \cdots & out_{11j} \\ \vdots & \ddots & \vdots \\ out_{1i1} & \cdots & out_{1ij} \\ \end{matrix} \right],\cdots, \left[ \begin{matrix} out_{n11} & \cdots & out_{n1j} \\ \vdots & \ddots & \vdots \\ out_{ni1} & \cdots & out_{nij} \\ \end{matrix} \right] )

3.稀疏矩陣的定義

列壓縮存儲

In [54]: mt=np.array([[1,2,0,0],[0,4,0,0],[0,0,0,3],[0,0,0,0]])

In [55]: mt
Out[55]: 
array([[1, 2, 0, 0],
       [0, 4, 0, 0],
       [0, 0, 0, 3],
       [0, 0, 0, 0]])

In [59]: cscm=sparse.csc_matrix(mt)

In [60]: cscm.data
Out[60]: array([1, 2, 4, 3], dtype=int32)

In [61]: cscm.indices
Out[61]: array([0, 0, 1, 2], dtype=int32)

In [62]: cscm.indptr
Out[62]: array([0, 1, 3, 3, 4], dtype=int32)

In [63]: cscm
Out[63]: 
<4x4 sparse matrix of type '<class 'numpy.int32'>'
    with 4 stored elements in Compressed Sparse Column format>

稀疏矩陣用于在矩陣中零元素占多數(shù)的情況下減少存儲和運(yùn)算開銷,零元素越多,提升越大。
稀疏矩陣有多種壓縮數(shù)據(jù)的方式,本文中因為會將稀疏矩陣用于右乘,該運(yùn)算下是按列提取向量,故采用列壓縮存儲。
可通過傳入一個稠密矩陣創(chuàng)建對應(yīng)的稀疏矩陣,但在原矩陣構(gòu)造出來會很龐大時不建議這么做,可通過直接構(gòu)造稀疏矩陣的關(guān)鍵參數(shù)來創(chuàng)建。

csc_matrix有三個關(guān)鍵參數(shù):
(1). data參數(shù)是存儲非零元素的值的一維數(shù)組,元素按先列后行排列;
(2). indices參數(shù)是存儲非零元素的列坐標(biāo)的一維數(shù)組,比如一個元素處在某一列的第一個位置,對應(yīng)的列坐標(biāo)就是0;
(3). indptr參數(shù)是存儲每列的首個非零元素在data中位置的一維數(shù)組,再額外加上data的長度;當(dāng)某一列沒有非零元素時,對應(yīng)的indptr元素和上一列相同,如果是第一列則為0

csc_matrixtoarraytodense方法分別可以將稀疏矩陣還原為稠密矩陣的ndarray形式和matrix形式。

4.基于稀疏矩陣的卷積運(yùn)算

由于先構(gòu)造稠密矩陣再轉(zhuǎn)換為稀疏矩陣的方式與使用稀疏矩陣減少存儲開銷的目的相違背,此處會通過構(gòu)造關(guān)鍵參數(shù)直接創(chuàng)建稀疏矩陣。

data參數(shù)的構(gòu)造較為簡單,權(quán)重矩陣中的非零元素就是卷積核的元素,每個位置卷積運(yùn)算的權(quán)重矩陣會被展開為列向量,csc_matrixdata的元素又是按先列后行排列的,所以只要將卷積核展開成的向量按位置總數(shù)重復(fù)拼接就行了;

indices參數(shù)的構(gòu)造需要了解numpy中元素的排列順序:
首先看起始位置,權(quán)重矩陣展開為列向量后,原本卷積核元素的位置如下所示:


圖上的數(shù)字標(biāo)識的即是展開為向量后元素的位置,numpy數(shù)組上對元素的訪問是從最后一個軸開始的,在RGB圖片形狀(h,w,c)下,2軸的索引每加1,展開后的索引加1,1軸的索引每加1,展開后的索引加c,0軸索引每加1,展開后的索引加w*c;
在橫向移動的過程中,每移動一步,展開后索引會全體增加step*c
在縱向移動的過程中,每移動一步,展開后索引會全體增加step*w*c
因此indices可以分解為三部分:起始位置索引+橫向移動帶來的索引偏移+縱向移動帶來的索引偏移。

indptr參數(shù)的構(gòu)造也很簡單,因為每列只有卷積核元素為非零元素(注意,即使卷積核元素可能為0,也要當(dāng)作非零元素),所以每列首個非零元素在data中的位置可以構(gòu)成以卷積核大小為步長的等差數(shù)列。

def conv4(array,kernel,step=1):
    #圖片的張數(shù)、高、寬
    n,h,w,c=array.shape
    #卷積核的高、寬、顏色通道
    kh,kw,kc=kernel.shape
    #縱向和橫向的移動步數(shù)
    hs,ws=(h-kh)//step+1,(w-kw)//step+1
    #1.非零值
    data=np.tile(kernel.ravel(),hs*ws)
    #2.行索引
    #(即卷積核元素在對應(yīng)的權(quán)重矩陣轉(zhuǎn)換成的向量中的位置)
    #(1)起始位置索引
    #由三個軸方向上的索引偏移量組合得到
    idx0=np.arange(0,kh*w*c,w*c).reshape((-1,1,1))
    idx1=np.arange(0,kw*c,c).reshape((1,-1,1))
    idx2=np.arange(0,c,1).reshape((1,1,-1))
    loc_base_=idx0+idx1+idx2
    loc_base=np.tile(loc_base_.ravel(),hs*ws)
    #(2)橫向和縱向移動的索引偏移
    loc_offset0=np.arange(0,hs*step*w*c,step*w*c).reshape((-1,1))
    loc_offset1=np.arange(0,ws*step*c,step*c).reshape((1,-1))
    loc_offset_=loc_offset0+loc_offset1
    loc_offset=loc_offset_.repeat(kh*kw*kc)
    indices=loc_base+loc_offset
    #3.列偏移
    #(即每列第一個非零值在values中的位置)
    indptr=np.arange(hs*ws+1)*(kh*kw*kc)
    #構(gòu)造稀疏矩陣
    weights=sparse.csc_matrix((data,indices,indptr))
    #圖片數(shù)據(jù)集變形
    array_=array.reshape((n,h*w*3))
    #稀疏矩陣乘法計算卷積結(jié)果
    result=array_*weights
    return result.reshape((-1,hs,ws)),weights

In [77]: start=time.clock()
    ...: out4,weights4=conv4(test_images,kernel)
    ...: print('\ntime used: %f s'%(time.clock()-start))

time used: 0.728124 s

In [77]: (out2==out4).all()
Out[77]: True

In [86]: weights4.data.size+weights4.indices.size+weights4.indptr.size
Out[86]: 49501

可以看到,雖然性能上稍有折損,但關(guān)鍵的內(nèi)存開銷降下來了,權(quán)重矩陣從原本的輸入大小*輸出大小個元素,減少到卷積核大小*輸出大小*2+輸出大小+1個元素。

在模型訓(xùn)練完畢后,可將所有卷積核轉(zhuǎn)換為稀疏矩陣存儲,避免每次前向傳播都要重新構(gòu)造。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容