日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 人文社科 > 生活经验 >内容正文

生活经验

pytorch源码解析:Python层 pytorchmodule源码

發(fā)布時(shí)間:2023/11/28 生活经验 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 pytorch源码解析:Python层 pytorchmodule源码 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

嘗試使用了pytorch,相比其他深度學(xué)習(xí)框架,pytorch顯得簡(jiǎn)潔易懂。花時(shí)間讀了部分源碼,主要結(jié)合簡(jiǎn)單例子帶著問(wèn)題閱讀,不涉及源碼中C拓展庫(kù)的實(shí)現(xiàn)。

一個(gè)簡(jiǎn)單例子

實(shí)現(xiàn)單層softmax二分類(lèi),輸入特征維度為4,輸出為2,經(jīng)過(guò)softmax函數(shù)得出輸入的類(lèi)別概率。代碼示意:定義網(wǎng)絡(luò)結(jié)構(gòu);使用SGD優(yōu)化;迭代一次,隨機(jī)初始化三個(gè)樣例,每個(gè)樣例四維特征,target分別為1,0,1;前向傳播,使用交叉熵計(jì)算loss;反向傳播,最后由優(yōu)化算法更新權(quán)重,完成一次迭代。

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F
  4. class Net(nn.Module):
  5. def __init__(self):
  6. super(Net, self).__init__()
  7. self.linear = nn.Linear(4, 2)
  8. def forward(self, input):
  9. out = F.softmax(self.linear(input))
  10. return out
  11. net = Net()
  12. sgd = torch.optim.SGD(net.parameters(), lr=0.001)
  13. for epoch in range(1):
  14. features = torch.autograd.Variable(torch.randn(3, 4), requires_grad=True)
  15. target = torch.autograd.Variable(torch.LongTensor([1, 0, 1]))
  16. sgd.zero_grad()
  17. out = net(features)
  18. loss = F.cross_entropy(out, target)
  19. loss.backward()
  20. sgd.step()

從上面的例子,帶著下面的問(wèn)題閱讀源碼:

  • pytorch的主要概念:Tensor、autograd、Variable、Function、Parameter、Module(Layers)、Optimizer;
  • 自定義Module如何組織網(wǎng)絡(luò)結(jié)構(gòu)和網(wǎng)絡(luò)參數(shù);
  • 前向傳播、反向傳播實(shí)現(xiàn)流程
  • 優(yōu)化算法類(lèi)如何實(shí)現(xiàn),如何和自定義Module聯(lián)系并更新參數(shù)。

pytorch的主要概念

pytorch的主要概念官網(wǎng)有很人性化的教程Deep Learning with PyTorch: A 60 Minute Blitz, 這里簡(jiǎn)單概括這些概念:

Tensor

類(lèi)似numpy的ndarrays,強(qiáng)化了可進(jìn)行GPU計(jì)算的特性,由C拓展模塊實(shí)現(xiàn)。如上面的torch.randn(3, 4) 返回一個(gè)3*4的Tensor。和numpy一樣,也有一系列的Operation,如

  1. x = torch.rand(5, 3)
  2. y = torch.rand(5, 3)
  3. print x + y
  4. print torch.add(x, y)
  5. print x.add_(y)

Varaiable與autograd

Variable封裝了Tensor,包括了幾乎所有的Tensor可以使用的Operation方法,主要使用在自動(dòng)求導(dǎo)(autograd),Variable類(lèi)繼承_C._VariableBase,由C拓展類(lèi)定義實(shí)現(xiàn)。
Variable是autograd的計(jì)算單元,Variable通過(guò)Function組織成函數(shù)表達(dá)式(計(jì)算圖):

  • data 為其封裝的tensor值
  • grad 為其求導(dǎo)后的值
  • creator 為創(chuàng)建該Variable的Function,實(shí)現(xiàn)中g(shù)rad_fn屬性則指向該Function。
    如:
    1. import torch
    2. from torch.autograd import Variable
    3. x = Variable(torch.ones(2, 2), requires_grad=True)
    4. y = x + 2
    5. print y.grad_fn
    6. print "before backward: ", x.grad
    7. y.backward()
    8. print "after backward: ", x.grad

    輸出結(jié)果:

    1. <torch.autograd.function.AddConstantBackward object at 0x7faa6f3bdd68>
    2. before backward: None
    3. after backward: Variable containing:
    4. 1
    5. [torch.FloatTensor of size 1x1]

    調(diào)用y的backward方法,則會(huì)對(duì)創(chuàng)建y的Function計(jì)算圖中所有requires_grad=True的Variable求導(dǎo)(這里的x)。例子中顯然dy/dx = 1。

Parameter

? ?Parameter 為Variable的一個(gè)子類(lèi),后面還會(huì)涉及,大概兩點(diǎn)區(qū)別:

  • 作為Module參數(shù)會(huì)被自動(dòng)加入到該Module的參數(shù)列表中;
  • 不能被volatile, 默認(rèn)require gradient。

Module

Module為所有神經(jīng)網(wǎng)絡(luò)模塊的父類(lèi),如開(kāi)始的例子,Net繼承該類(lèi),____init____中指定網(wǎng)絡(luò)結(jié)構(gòu)中的模塊,并重寫(xiě)forward方法實(shí)現(xiàn)前向傳播得到指定輸入的輸出值,以此進(jìn)行后面loss的計(jì)算和反向傳播。

Optimizer

Optimizer是所有優(yōu)化算法的父類(lèi)(SGD、Adam、...),____init____中傳入網(wǎng)絡(luò)的parameters, 子類(lèi)實(shí)現(xiàn)父類(lèi)step方法,完成對(duì)parameters的更新。

自定義Module

該部分說(shuō)明自定義的Module是如何組織定義在構(gòu)造函數(shù)中的子Module,以及自定義的parameters的保存形式,eg:

  1. class Net(nn.Module):
  2. def __init__(self):
  3. super(Net, self).__init__()
  4. self.linear = nn.Linear(4, 2)
  5. def forward(self, input):
  6. out = F.softmax(self.linear(input))
  7. return out

首先看構(gòu)造函數(shù),Module的構(gòu)造函數(shù)初始化了Module的基本屬性,這里關(guān)注_parameters和_modules,兩個(gè)屬性初始化為OrderedDict(),pytorch重寫(xiě)的有序字典類(lèi)型。_parameters保存網(wǎng)絡(luò)的所有參數(shù),_modules保存當(dāng)前Module的子Module。
module.py:

  1. class Module(object):
  2. def __init__(self):
  3. self._parameters = OrderedDict()
  4. self._modules = OrderedDict()
  5. ...

下面來(lái)看自定義Net類(lèi)中self.linear = nn.Linear(4, 2)語(yǔ)句和_modules、_parameters如何產(chǎn)生聯(lián)系,或者self.linear及其參數(shù)如何被添加到_modules、_parameters字典中。答案在Module的____setattr____方法,該P(yáng)ython內(nèi)建方法會(huì)在類(lèi)的屬性被賦值時(shí)調(diào)用。
module.py:

  1. def __setattr__(self, name, value):
  2. def remove_from(*dicts):
  3. for d in dicts:
  4. if name in d:
  5. del d[name]
  6. params = self.__dict__.get('_parameters')
  7. if isinstance(value, Parameter): # ----------- <1>
  8. if params is None:
  9. raise AttributeError(
  10. "cannot assign parameters before Module.__init__() call")
  11. remove_from(self.__dict__, self._buffers, self._modules)
  12. self.register_parameter(name, value)
  13. elif params is not None and name in params:
  14. if value is not None:
  15. raise TypeError("cannot assign '{}' as parameter '{}' "
  16. "(torch.nn.Parameter or None expected)"
  17. .format(torch.typename(value), name))
  18. self.register_parameter(name, value)
  19. else:
  20. modules = self.__dict__.get('_modules')
  21. if isinstance(value, Module):# ----------- <2>
  22. if modules is None:
  23. raise AttributeError(
  24. "cannot assign module before Module.__init__() call")
  25. remove_from(self.__dict__, self._parameters, self._buffers)
  26. modules[name] = value
  27. elif modules is not None and name in modules:
  28. if value is not None:
  29. raise TypeError("cannot assign '{}' as child module '{}' "
  30. "(torch.nn.Module or None expected)"
  31. .format(torch.typename(value), name))
  32. modules[name] = value
  33. ......

調(diào)用self.linear = nn.Linear(4, 2)時(shí),父類(lèi)____setattr____被調(diào)用,參數(shù)name為“l(fā)inear”, value為nn.Linear(4, 2),內(nèi)建的Linear類(lèi)同樣是Module的子類(lèi)。所以<2>中的判斷為真,接著modules[name] = value,該linear被加入_modules字典。
同樣自定義Net類(lèi)的參數(shù)即為其子模塊Linear的參數(shù),下面看Linear的實(shí)現(xiàn):
linear.py:

  1. class Linear(Module):
  2. def __init__(self, in_features, out_features, bias=True):
  3. super(Linear, self).__init__()
  4. self.in_features = in_features
  5. self.out_features = out_features
  6. self.weight = Parameter(torch.Tensor(out_features, in_features))
  7. if bias:
  8. self.bias = Parameter(torch.Tensor(out_features))
  9. else:
  10. self.register_parameter('bias', None)
  11. self.reset_parameters()
  12. def reset_parameters(self):
  13. stdv = 1. / math.sqrt(self.weight.size(1))
  14. self.weight.data.uniform_(-stdv, stdv)
  15. if self.bias is not None:
  16. self.bias.data.uniform_(-stdv, stdv)
  17. def forward(self, input):
  18. return F.linear(input, self.weight, self.bias)

同樣繼承Module類(lèi),____init____中參數(shù)為輸入輸出維度,是否需要bias參數(shù)。在self.weight = Parameter(torch.Tensor(out_features, in_features))的初始化時(shí),同樣會(huì)調(diào)用父類(lèi)Module的____setattr____, name為“weight”,value為Parameter,此時(shí)<1>判斷為真,調(diào)用self.register_parameter(name, value),該方法中對(duì)參數(shù)進(jìn)行合法性校驗(yàn)后放入self._parameters字典中。

Linear在reset_parameters方法對(duì)權(quán)重進(jìn)行了初始化。

最終可以得出結(jié)論自定義的Module以樹(shù)的形式組織子Module,子Module及其參數(shù)以字典的方式保存。

前向傳播、反向傳播

前向傳播

例子中out = net(features)實(shí)現(xiàn)了網(wǎng)絡(luò)的前向傳播,該語(yǔ)句會(huì)調(diào)用Module類(lèi)的forward方法,該方法被繼承父類(lèi)的子類(lèi)實(shí)現(xiàn)。net(features)使用對(duì)象作為函數(shù)調(diào)用,會(huì)調(diào)用Python內(nèi)建的____call____方法,Module重寫(xiě)了該方法。

module.py:

  1. def __call__(self, *input, **kwargs):
  2. for hook in self._forward_pre_hooks.values():
  3. hook(self, input)
  4. result = self.forward(*input, **kwargs)
  5. for hook in self._forward_hooks.values():
  6. hook_result = hook(self, input, result)
  7. if hook_result is not None:
  8. raise RuntimeError(
  9. "forward hooks should never return any values, but '{}'"
  10. "didn't return None".format(hook))
  11. if len(self._backward_hooks) > 0:
  12. var = result
  13. while not isinstance(var, Variable):
  14. var = var[0]
  15. grad_fn = var.grad_fn
  16. if grad_fn is not None:
  17. for hook in self._backward_hooks.values():
  18. wrapper = functools.partial(hook, self)
  19. functools.update_wrapper(wrapper, hook)
  20. grad_fn.register_hook(wrapper)
  21. return result

____call____方法中調(diào)用result = self.forward(*input, **kwargs)前后會(huì)查看有無(wú)hook函數(shù)需要調(diào)用(預(yù)處理和后處理)。
例子中Net的forward方法中out = F.softmax(self.linear(input)),同樣會(huì)調(diào)用self.linear的forward方法F.linear(input, self.weight, self.bias)進(jìn)行矩陣運(yùn)算(仿射變換)。
functional.py:

  1. def linear(input, weight, bias=None):
  2. if input.dim() == 2 and bias is not None:
  3. # fused op is marginally faster
  4. return torch.addmm(bias, input, weight.t())
  5. output = input.matmul(weight.t())
  6. if bias is not None:
  7. output += bias
  8. return output

最終經(jīng)過(guò)F.softmax,得到前向輸出結(jié)果。F.softmax和F.linear類(lèi)似前面說(shuō)到的Function(Parameters的表達(dá)式或計(jì)算圖)。

反向傳播

得到前向傳播結(jié)果后,計(jì)算loss = F.cross_entropy(out, target),接下來(lái)反向傳播求導(dǎo)數(shù)d(loss)/d(weight)和d(loss)/d(bias):

loss.backward()

backward()方法同樣底層由C拓展,這里暫不深入,調(diào)用該方法后,loss計(jì)算圖中的所有Variable(這里linear的weight和bias)的grad被求出。

Optimizer參數(shù)更新

在計(jì)算出參數(shù)的grad后,需要根據(jù)優(yōu)化算法對(duì)參數(shù)進(jìn)行更新,不同的優(yōu)化算法有不同的更新策略。
optimizer.py:

  1. class Optimizer(object):
  2. def __init__(self, params, defaults):
  3. if isinstance(params, Variable) or torch.is_tensor(params):
  4. raise TypeError("params argument given to the optimizer should be "
  5. "an iterable of Variables or dicts, but got " +
  6. torch.typename(params))
  7. self.state = defaultdict(dict)
  8. self.param_groups = list(params)
  9. ......
  10. def zero_grad(self):
  11. """Clears the gradients of all optimized :class:`Variable` s."""
  12. for group in self.param_groups:
  13. for p in group['params']:
  14. if p.grad is not None:
  15. if p.grad.volatile:
  16. p.grad.data.zero_()
  17. else:
  18. data = p.grad.data
  19. p.grad = Variable(data.new().resize_as_(data).zero_())
  20. def step(self, closure):
  21. """Performs a single optimization step (parameter update).
  22. Arguments:
  23. closure (callable): A closure that reevaluates the model and
  24. returns the loss. Optional for most optimizers.
  25. """
  26. raise NotImplementedError

Optimizer在init中將傳入的params保存到self.param_groups,另外兩個(gè)重要的方法zero_grad負(fù)責(zé)將參數(shù)的grad置零方便下次計(jì)算,step負(fù)責(zé)參數(shù)的更新,由子類(lèi)實(shí)現(xiàn)。
以列子中的sgd = torch.optim.SGD(net.parameters(), lr=0.001)為例,其中net.parameters()返回Net參數(shù)的迭代器,為待優(yōu)化參數(shù);lr指定學(xué)習(xí)率。
SGD.py:

  1. class SGD(Optimizer):
  2. def __init__(self, params, lr=required, momentum=0, dampening=0,
  3. weight_decay=0, nesterov=False):
  4. defaults = dict(lr=lr, momentum=momentum, dampening=dampening,
  5. weight_decay=weight_decay, nesterov=nesterov)
  6. if nesterov and (momentum <= 0 or dampening != 0):
  7. raise ValueError("Nesterov momentum requires a momentum and zero dampening")
  8. super(SGD, self).__init__(params, defaults)
  9. def __setstate__(self, state):
  10. super(SGD, self).__setstate__(state)
  11. for group in self.param_groups:
  12. group.setdefault('nesterov', False)
  13. def step(self, closure=None):
  14. """Performs a single optimization step.
  15. Arguments:
  16. closure (callable, optional): A closure that reevaluates the model
  17. and returns the loss.
  18. """
  19. loss = None
  20. if closure is not None:
  21. loss = closure()
  22. for group in self.param_groups:
  23. weight_decay = group['weight_decay']
  24. momentum = group['momentum']
  25. dampening = group['dampening']
  26. nesterov = group['nesterov']
  27. for p in group['params']:
  28. if p.grad is None:
  29. continue
  30. d_p = p.grad.data
  31. if weight_decay != 0:
  32. d_p.add_(weight_decay, p.data)
  33. if momentum != 0:
  34. param_state = self.state[p]
  35. if 'momentum_buffer' not in param_state:
  36. buf = param_state['momentum_buffer'] = d_p.clone()
  37. else:
  38. buf = param_state['momentum_buffer']
  39. buf.mul_(momentum).add_(1 - dampening, d_p)
  40. if nesterov:
  41. d_p = d_p.add(momentum, buf)
  42. else:
  43. d_p = buf
  44. p.data.add_(-group['lr'], d_p)
  45. return loss

SGD的step方法中,判斷是否使用權(quán)重衰減和動(dòng)量更新,如果不使用,直接更新權(quán)重param := param - lr * d(param)。例子中調(diào)用sgd.step()后完成一次epoch。這里由于傳遞到Optimizer的參數(shù)集是可更改(mutable)的,step中對(duì)參數(shù)的更新同樣是Net中參數(shù)的更新。

小結(jié)

到此,根據(jù)一個(gè)簡(jiǎn)單例子閱讀了pytorch中Python實(shí)現(xiàn)的部分源碼,沒(méi)有深入到底層Tensor、autograd等部分的C拓展實(shí)現(xiàn),后面再繼續(xù)讀一讀C拓展部分的代碼。


轉(zhuǎn)自鏈接:https://www.jianshu.com/p/f5eb8c2e671c

總結(jié)

以上是生活随笔為你收集整理的pytorch源码解析:Python层 pytorchmodule源码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。