Source code for chainer.link

import copy
import warnings

import numpy
import six

from chainer import cuda
from chainer import variable





[docs]class Chain(Link): """Composable link with object-like interface. Composability is one of the most important features of neural nets. Neural net models consist of many reusable fragments, and each model itself might be embedded into a larger learnable system. Chain enables us to write a neural net based on composition, without bothering about routine works like collecting parameters, serialization, copying the structure with parameters shared, etc. This class actually provides a way to compose one or more links into one structure. A chain can contain one or more *child links*. Child link is a link registered to the chain with its own name. The child link is stored to an attribute of the chain with the name. User can write a whole model or a fragment of neural nets as a child class of Chain. Each chain itself is also a link. Therefore, one can combine chains into higher-level chains. In this way, links and chains construct a *link hierarchy*. Link hierarchy forms a tree structure, where each node is identified by the path from the root. The path is represented by a string like a file path in UNIX, consisting of names of nodes on the path, joined by slashes ``/``. .. admonition:: Example This is a simple example of custom chain definition. Chainer itself also provides some chains defined under the :mod:`~chainer.links` module. They might serve as examples, too. Consider we want to define a multi-layer perceptron consisting of two hidden layers with rectifiers as activation functions. We can use the :class:`~chainer.links.Linear` link as a building block:: import chainer import chainer.functions as F import chainer.links as L class MultiLayerPerceptron(chainer.Chain): def __init__(self, n_in, n_hidden, n_out): # Create and register three layers for this MLP super(MultiLayerPerceptron, self).__init__( layer1=L.Linear(n_in, n_hidden), layer2=L.Linear(n_hidden, n_hidden), layer3=L.Linear(n_hidden, n_out), ) def __call__(self, x): # Forward propagation h1 = F.relu(self.layer1(x)) h2 = F.relu(self.layer2(h1)) return self.layer3(h2) Child links are registered via the initializer method. They also can be registered by the :meth:`add_link` method. The forward propagation is often implemented as The ``__call__`` operator as the above example, though it is not mandatory. Args: links: Child links. The keywords are used as their names. The names are also set to the links. """ def __init__(self, **links): super(Chain, self).__init__() self._children = [] for name, link in six.iteritems(links): self.add_link(name, link)
[docs] def __getitem__(self, name): """Equivalent to getattr.""" return getattr(self, name)
def copy(self): ret = super(Chain, self).copy() ret._children = list(ret._children) d = ret.__dict__ for name in ret._children: # copy child links recursively copied = d[name].copy() copied.name = name d[name] = copied return ret def to_cpu(self): super(Chain, self).to_cpu() d = self.__dict__ for name in self._children: d[name].to_cpu() return self def to_gpu(self, device=None): with cuda.get_device(device): super(Chain, self).to_gpu() d = self.__dict__ for name in self._children: d[name].to_gpu() return self def params(self): for param in super(Chain, self).params(): yield param d = self.__dict__ for name in self._children: for param in d[name].params(): yield param def namedparams(self): for ret in super(Chain, self).namedparams(): yield ret d = self.__dict__ for name in self._children: prefix = '/' + name for path, param in d[name].namedparams(): yield prefix + path, param def links(self, skipself=False): if not skipself: yield self d = self.__dict__ for name in self._children: for link in d[name].links(): yield link def namedlinks(self, skipself=False): if not skipself: yield '/', self d = self.__dict__ for name in self._children: child = d[name] prefix = '/' + name yield prefix, child for path, link in d[name].namedlinks(True): yield prefix + path, link def children(self): d = self.__dict__ for name in self._children: yield d[name] def copyparams(self, link): super(Chain, self).copyparams(link) src = link.__dict__ dst = self.__dict__ for name in self._children: dst[name].copyparams(src[name]) def addgrads(self, link): super(Chain, self).addgrads(link) src = link.__dict__ dst = self.__dict__ for name in self._children: dst[name].addgrads(src[name]) def serialize(self, serializer): super(Chain, self).serialize(serializer) d = self.__dict__ for name in self._children: d[name].serialize(serializer[name])
[docs]class ChainList(Link): """Composable link with list-like interface. This is another example of compositional link. Unlike :class:`Chain`, this class can be used like a list of child links. Each child link is indexed by a non-negative integer, and it maintains the current number of registered child links. The :meth:`add_link` method inserts a new link at the end of the list. It is useful to write a chain with arbitrary number of child links, e.g. an arbitrarily deep multi-layer perceptron. Note that this class does not implement all methods of :class:`list`. Args: links: Initial child links. """ def __init__(self, *links): super(ChainList, self).__init__() self._children = [] for link in links: self.add_link(link)
[docs] def __getitem__(self, index): """Returns the child at given index. Args: index (int): Index of the child in the list. Returns: Link: The ``index``-th child link. """ return self._children[index]
def __iter__(self): return iter(self._children)
[docs] def __len__(self): """Returns a number of children.""" return len(self._children)
def copy(self): ret = super(ChainList, self).copy() ret._children = list(ret._children) # copy children = ret._children for i, child in enumerate(children): child = child.copy() child.name = str(i) children[i] = child return ret def to_cpu(self): super(ChainList, self).to_cpu() for link in self._children: link.to_cpu() return self def to_gpu(self, device=None): with cuda.get_device(device): super(ChainList, self).to_gpu() for link in self._children: link.to_gpu() return self def params(self): for param in super(ChainList, self).params(): yield param for link in self._children: for param in link.params(): yield param def namedparams(self): for ret in super(ChainList, self).namedparams(): yield ret for idx, link in enumerate(self._children): prefix = '/%d' % idx for path, param in link.namedparams(): yield prefix + path, param def links(self, skipself=False): if not skipself: yield self for child in self._children: for link in child.links(): yield link def namedlinks(self, skipself=False): if not skipself: yield '/', self for idx, child in enumerate(self._children): prefix = '/%d' % idx yield prefix, child for path, link in child.namedlinks(True): yield prefix + path, link def children(self): for child in self._children: yield child def copyparams(self, link): super(ChainList, self).copyparams(link) for idx, child in enumerate(self._children): child.copyparams(link[idx]) def addgrads(self, link): super(ChainList, self).addgrads(link) for idx, child in enumerate(self._children): child.addgrads(link[idx]) def serialize(self, serializer): super(ChainList, self).serialize(serializer) for idx, child in enumerate(self._children): child.serialize(serializer['%d' % idx])