用PyTorch也有個把年頭了,可是偶爾仍是會出現各類各樣的bug,當bug積累到必定數量就會引發質變——寫成一個筆記來長記性……bash
0.4
以後的版本能夠直接用Tensor代替Variable了,不須要再繁瑣得包裹一層Variable
。網絡
tensor的轉置有不少方式,好比.transpose
、.transpose_
是最經常使用的(帶下劃線的是在原tensor上作改變),可是這個轉置方式有個限制就是僅能對兩個維度進行操做,是個「根正苗紅」的轉置函數。app
有時候須要將更多的維度進行交換,這個時候就能夠用.permuate
函數,該函數接受一個維度列表,而後根據列表中的維度的排列順序更新tensor的維度排列。機器學習
升維.unsqueeze
是在指定的維度上插入一維,可是數據實際上沒有發生變化。好比下面得示例:函數
In [1]: a
Out[1]:
tensor([[[-0.7908, -1.4576, -0.3251],
[-1.2053, 0.3667, 0.9423],
[ 0.0517, 0.6051, -0.1360],
[ 0.8666, -1.4679, -0.4511]]])
In [2]: a.shape
Out[2]: torch.Size([1, 4, 3])
In [3]: a.unsqueeze(2).shape
Out[3]: torch.Size([1, 4, 1, 3])
複製代碼
降維.squeeze
是升維得逆操做,會消除全部維度爲1的維度:學習
In [1]: a.shape
Out[1]: torch.Size([1, 4, 3])
In [2]: a.squeeze().shape
Out[2]: torch.Size([4, 3])
複製代碼
一個Tensor執行轉置操做後,實際上數據並無發生任何變化,只是讀取數據的順序變化了。這是一種節約空間和運算的方法。ui
不過轉置以後的數據若是須要進行.view
等操做的話,因爲數據在邏輯上並不連續,所以須要手動調用contiguous
讓數據恢復連續存儲的模樣。spa
這個是早期常常會遇到的狀況,咋一會要FloatTensor
,一會就要LongTensor
了?要解決這個問題,仍是須要深刻了解不一樣的loss function到底在作啥。調試
二分類的交叉熵,計算公式以下:code
這樣就不難看出 和
都須要是
FloatTensor
了。若是是不一樣的tensor進行運算會出錯:
Expected object of type torch.FloatTensor but found type torch.LongTensor
複製代碼
上面的BCELoss
是計算二分類狀況下的損失函數,那麼當遇到多分類問題的時候,也想用交叉熵來計算的話,就須要用到CrossEntropyLoss
了。
不管是二分類仍是多分類,均可以用最簡單的L2損失來計算,可是若是使用
Sigmoid
做爲激活函數的話容易致使梯度消失,致使效果很差
權值不初始化會出事的……
有時候會遇到一些結構清奇的網絡,各類參數糾纏在一塊兒,所以torch自帶的函數可能不夠用,那就須要自定義網絡結構了。通常會從最底層的矩陣開始構建,就像tensorflow同樣。好比定義一個不帶偏置項的全鏈接層能夠用nn.Linear(xxx, xxx, bias=False)
。但特殊狀況下甚至連全鏈接層內部的細節都要本身定義,這個時候就須要參考torch的源碼了,好比全鏈接層的部分源碼:
class Linear(Module):
def __init__(self, in_features, out_features, bias=True):
super(Linear, self).__init__()
self.in_features = in_features
self.out_features = out_features
self.weight = Parameter(torch.Tensor(out_features, in_features))
if bias:
self.bias = Parameter(torch.Tensor(out_features))
else:
self.register_parameter('bias', None)
self.reset_parameters()
def reset_parameters(self):
stdv = 1. / math.sqrt(self.weight.size(1))
self.weight.data.uniform_(-stdv, stdv)
if self.bias is not None:
self.bias.data.uniform_(-stdv, stdv)
def forward(self, input):
return F.linear(input, self.weight, self.bias)
複製代碼
在這個例子中已經能學到不少了,這裏重點參考Parameters
的使用(out_feature在前)和權值初始化函數reset_parameters
的內部細節。
以前一次機器學習的做業,相同的模型,PyTorch版始終達不到Tensorflow版的準確率,大概就是權值初始化二者存在差別吧。
以前有用過本身先定義了一個簡單的底層網絡,方便上層網絡的構建。這樣定義出來的網絡在CPU上運行的好好的,可是當我把模型遷移到GPU上時卻發現模型出現了類型不符的錯誤:
Expected object of type torch.FloatTensor but found type torch.cuda.FloatTensor for argument
複製代碼
後來在調試的時候發現對上層網絡調用.cuda
後,下層網絡卻仍是處於device(type='cpu')
!雖然解決這個問題最簡單的方法就是把底層網絡的定義放到上層網絡中,可是這樣不利於代碼的維護,而且在不一樣網絡中共享模塊也會變得很複雜。
PyTorch在CPU和GPU數據上的遷移的確沒有tensorflow來的方便……
在GitHub上看到不少相似的問題的解決方法,其中有一個點子很不錯:
if torch.cuda.available():
import torch.cuda as t
else:
import torch as t
複製代碼
若是一開始寫代碼的時候就考慮到自動CUDA的話,這的確是個好辦法,若是已經寫了臃腫的代碼再改寫仍是會很繁瑣。
最終我選擇的方案仍是重寫.cuda
函數:
def cuda(self, device=None):
r"""Moves all model parameters and buffers to the GPU..."""
return self._apply(lambda t: t.cuda(device))
複製代碼
在.cuda
函數中對子模塊也調用.cuda
就能夠了。
若是一個數據已經遷移到GPU上了,那麼若是要將運算結果轉爲符合numpy特性的數據就不是這麼直接的了,通常會報錯:
TypeError: can't convert CUDA tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first. 複製代碼
解決方案報錯信息已經給了。固然還能夠將以後須要numpy來幫忙的運算改爲PyTorch支持的運算,這樣速度還更快呢,畢竟條條大路通羅馬。