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

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

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

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

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

卷積核

卷積核的三個(gè)軸分別對(duì)應(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ù)集的四個(gè)軸分別對(duì)應(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對(duì)應(yīng)樣本庵朝,axis1對(duì)應(yīng)圖片的高,axis2對(duì)應(yīng)圖片的寬,axis3對(duì)應(yīng)顏色通道九府。
從中抽取一張圖片椎瘟,則axis0,axis1,axis2分別對(duì)應(yīng)高,寬,通道

單圖卷積運(yùn)算

#array: 單張圖片數(shù)據(jù)
#kernel: 卷積核
#step: 步長(zhǎng)
def conv1(array,kernel,step=1):
    #圖片的高和寬
    h,w=array.shape[:2]
    #卷積核的高和寬
    kh,kw=kernel.shape[:2]
    #縱向和橫向的移動(dòng)步數(shù)
    hs,ws=(h-kh)//step+1,(w-kw)//step+1
    #初始化輸出數(shù)組
    out=np.empty((hs,ws))
    #縱向滑動(dòng)卷積核
    for i in range(hs):
        #卷積核的縱向覆蓋范圍
        start0,end0=i*step,i*step+kh
        #橫向滑動(dòng)卷積核
        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)行點(diǎn)積運(yùn)算侄旬。

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

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

批量卷積運(yùn)算

#array: 圖片數(shù)據(jù)集
#kernel: 卷積核
#step: 步長(zhǎng)
def conv2(array,kernel,step=1):
    #圖片的張數(shù)仇冯、高、寬
    n,h,w=array.shape[:3]
    #卷積核的高族操、寬苛坚、顏色通道
    kh,kw,kc=kernel.shape[:3]
    #縱向和橫向的移動(dòng)步數(shù)
    hs,ws=(h-kh)//step+1,(w-kw)//step+1
    #初始化輸出數(shù)組
    out=np.empty((n,hs,ws))
    #縱向滑動(dòng)卷積核
    for i in range(hs):
        #卷積核的縱向覆蓋范圍
        start0,end0=i*step,i*step+kh
        #橫向滑動(dòng)卷積核
        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可以用于完成 向量點(diǎn)積,矩陣與向量的乘積色难,矩陣乘法 等運(yùn)算泼舱。

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

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

(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]
    #縱向和橫向的移動(dòng)步數(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))
    #矩陣乘法計(jì)算卷積結(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個(gè)元素的大小,隨著圖片分辨率的提高今阳,開銷會(huì)增大到無法接受师溅,乃至有性能倒退的可能。
考慮到卷積運(yùn)算的權(quán)重矩陣有大量的零元素盾舌,這些元素對(duì)計(jì)算沒有貢獻(xiàn)墓臭,應(yīng)當(dāng)采用稀疏矩陣的形式進(jìn)行處理。

對(duì)基于滑窗的計(jì)算方式進(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.稀疏矩陣的定義

列壓縮存儲(chǔ)

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ù)的情況下減少存儲(chǔ)和運(yùn)算開銷起便,零元素越多,提升越大窖维。
稀疏矩陣有多種壓縮數(shù)據(jù)的方式榆综,本文中因?yàn)闀?huì)將稀疏矩陣用于右乘,該運(yùn)算下是按列提取向量铸史,故采用列壓縮存儲(chǔ)鼻疮。
可通過傳入一個(gè)稠密矩陣創(chuàng)建對(duì)應(yīng)的稀疏矩陣,但在原矩陣構(gòu)造出來會(huì)很龐大時(shí)不建議這么做琳轿,可通過直接構(gòu)造稀疏矩陣的關(guān)鍵參數(shù)來創(chuàng)建判沟。

csc_matrix有三個(gè)關(guān)鍵參數(shù):
(1). data參數(shù)是存儲(chǔ)非零元素的值的一維數(shù)組,元素按先列后行排列崭篡;
(2). indices參數(shù)是存儲(chǔ)非零元素的列坐標(biāo)的一維數(shù)組挪哄,比如一個(gè)元素處在某一列的第一個(gè)位置,對(duì)應(yīng)的列坐標(biāo)就是0琉闪;
(3). indptr參數(shù)是存儲(chǔ)每列的首個(gè)非零元素在data中位置的一維數(shù)組迹炼,再額外加上data的長(zhǎng)度;當(dāng)某一列沒有非零元素時(shí)颠毙,對(duì)應(yīng)的indptr元素和上一列相同斯入,如果是第一列則為0

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

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

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

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

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


圖上的數(shù)字標(biāo)識(shí)的即是展開為向量后元素的位置厅克,numpy數(shù)組上對(duì)元素的訪問是從最后一個(gè)軸開始的赔退,在RGB圖片形狀(h,w,c)下,2軸的索引每加1证舟,展開后的索引加1硕旗,1軸的索引每加1,展開后的索引加c女责,0軸索引每加1漆枚,展開后的索引加w*c
在橫向移動(dòng)的過程中抵知,每移動(dòng)一步墙基,展開后索引會(huì)全體增加step*c
在縱向移動(dòng)的過程中刷喜,每移動(dòng)一步残制,展開后索引會(huì)全體增加step*w*c
因此indices可以分解為三部分:起始位置索引+橫向移動(dòng)帶來的索引偏移+縱向移動(dòng)帶來的索引偏移掖疮。

indptr參數(shù)的構(gòu)造也很簡(jiǎn)單初茶,因?yàn)槊苛兄挥芯矸e核元素為非零元素(注意,即使卷積核元素可能為0浊闪,也要當(dāng)作非零元素)恼布,所以每列首個(gè)非零元素在data中的位置可以構(gòu)成以卷積核大小為步長(zhǎng)的等差數(shù)列。

def conv4(array,kernel,step=1):
    #圖片的張數(shù)搁宾、高折汞、寬
    n,h,w,c=array.shape
    #卷積核的高、寬盖腿、顏色通道
    kh,kw,kc=kernel.shape
    #縱向和橫向的移動(dòng)步數(shù)
    hs,ws=(h-kh)//step+1,(w-kw)//step+1
    #1.非零值
    data=np.tile(kernel.ravel(),hs*ws)
    #2.行索引
    #(即卷積核元素在對(duì)應(yīng)的權(quán)重矩陣轉(zhuǎn)換成的向量中的位置)
    #(1)起始位置索引
    #由三個(gè)軸方向上的索引偏移量組合得到
    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)橫向和縱向移動(dòng)的索引偏移
    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.列偏移
    #(即每列第一個(gè)非零值在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))
    #稀疏矩陣乘法計(jì)算卷積結(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)重矩陣從原本的輸入大小*輸出大小個(gè)元素堕伪,減少到卷積核大小*輸出大小*2+輸出大小+1個(gè)元素揖庄。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疙筹,一起剝皮案震驚了整個(gè)濱河市富俄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌而咆,老刑警劉巖霍比,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異暴备,居然都是意外死亡悠瞬,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門蚜点,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罩阵,“玉大人穿仪,你說我怎么就攤上這事×柰猓” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵涛浙,是天一觀的道長(zhǎng)康辑。 經(jīng)常有香客問我,道長(zhǎng)轿亮,這世上最難降的妖魔是什么疮薇? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮我注,結(jié)果婚禮上惦辛,老公的妹妹穿的比我還像新娘。我一直安慰自己仓手,他們只是感情好胖齐,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著嗽冒,像睡著了一般呀伙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上添坊,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天剿另,我揣著相機(jī)與錄音,去河邊找鬼贬蛙。 笑死雨女,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的阳准。 我是一名探鬼主播氛堕,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼野蝇!你這毒婦竟也來了讼稚?” 一聲冷哼從身側(cè)響起括儒,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锐想,沒想到半個(gè)月后帮寻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赠摇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年固逗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片藕帜。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抒蚜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耘戚,到底是詐尸還是另有隱情嗡髓,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布收津,位于F島的核電站饿这,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏撞秋。R本人自食惡果不足惜长捧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吻贿。 院中可真熱鬧串结,春花似錦、人聲如沸舅列。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)帐要。三九已至把敞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間榨惠,已是汗流浹背奋早。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赠橙,地道東北人耽装。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像期揪,于是被迫代替她去往敵國(guó)和親掉奄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345