対象者
前回の記事の続きです。
本記事ではこれからコーディングしていくための準備をします。
自分で実装したいけどなかなか...という方はぜひ見ていってください。
(基本的に複雑なテクニックとかはぼくが使えませんのでご安心ください)
コーディング環境はjupyter notebookです。
他でモジュールレベルで管理する場合はimport
などのコマンドのコメントを外してください。
もし動かなかったらコメントで教えてください...
次回の記事はこちら
目次
レイヤーモジュールのコード準備
まずはベースとなる入れ物を作ります。
baselayer.py
import numpy as np
class BaseLayer():
"""
全ての元となるレイヤークラス
中間層と出力層で共通する処理を記述する。
"""
def __init__(self):
pass
def forward(self):
"""
順伝播の実装
"""
pass
def backward(self):
"""
逆伝播の実装
"""
pass
def update(self):
"""
パラメータ学習の実装
"""
pass
middlelayer.py
import numpy as np
#import .baselayer import BaseLayer
class MiddleLayer(BaseLayer):
"""
中間層クラス
入力層も実装上は中間層の一つとして取り扱います。
"""
pass
outputlayer.py
import numpy as np
#from .baselayer import BaseLayer
class OutputLayer(BaseLayer):
"""
出力層クラス
"""
pass
レイヤーマネージャのコード準備
ここでは、レイヤーモジュールを取り扱うためのマネージャを準備します。
最後に一つにまとめてコードを載せます。もしかしたら途中のコードには変更忘れなどがあるかもしれませんのでご注意ください。
基本的にはPythonのList
型やDictionary
型を意識して作っていきます。
layermanager.py
import numpy as np
#from .layer import *
class _TypeManager():
"""
層の種類に関するマネージャクラス
"""
N_TYPE = 2 # 層の種類数
MIDDLE = 0 # 中間層のナンバリング
OUTPUT = 1 # 出力層のナンバリング
class LayerManager(_TypeManager):
"""
層を管理するためのマネージャクラス
"""
def __init__(self):
pass
_TypeManager
クラスは層の種類の数とそれぞれのナンバリングを保持させるクラスです。最初についている_
(アンダーバー)は外部モジュールからのアクセスを制限する、他の言語で言うとprivate
なクラスです。
とはいえPythonには完全なprivate
属性は存在しないのでアクセス自体は可能だったりします。
LayerManager
クラスはDictionary
型を意識して、層のリストを保持したり追加・削除などを行えるようにします。
また、_TypeManager
を継承させておきます。
ちなみにlayer
モジュールはLayer
関連のモジュールをまとめてインポートするためのモジュールです。
layer.py
from .middlelayer import *
from .outputlayer import *
from ._layererror import *
_layererror
モジュールは後述しますが、レイヤーで発生するエラーをまとめたモジュールにする予定です。
特殊メソッドの実装
まずはLayerManager
クラスの特殊メソッドを実装しておきます。特殊メソッド一覧については公式ドキュメントをご覧ください。
__init__
メソッドの実装
では、LayerManager
クラスの__init__
メソッドを実装します。
そもそも__init__
メソッドとはPythonの特殊メソッドの一つで、クラスのインスタンス生成時に呼ばれるメソッドの一つです。
つまり、LayerManager
クラスを生成されたときに行って欲しい動作を記述します。
ここでは
- 層のリストを保持
- 層の名前のリストを保持
- 種類別の層の数を保持
をして欲しいので、そのように実装します。
`__init__`の実装
def __init__(self):
self.__layer_list = [] # レイヤーのリスト
self.__name_list = [] # 各レイヤーの名前リスト
self.__ntype = np.zeros(self.N_TYPE) # 種類別レイヤーの数
もちろん外部からは触られたくないので__
(アンダースコア二つ)でprivate
化しておきましょう。
__repr__
メソッドと__str__
メソッドの実装
__repr__
メソッドはPythonのビルドイン関数の一つであるrepr
関数から呼び出される「公式な」文字列表現です。
「公式な」とか言いますが、要は詳細な情報ということです。
__str__
メソッドはPythonのビルドイン関数の一つであるstr
関数や、組み込み関数であるformat
関数やprint
関数から呼び出される「非公式な」文字列表現です。
「非公式な」の意味はつまり見やすい情報ということです。
`__repr__`メソッドと`__str__`メソッドの実装
def __repr__(self):
layerRepr= "layer_list: " + repr(self.__layer_list)
nameRepr = "name_list: " + repr(self.__name_list)
ntypeRepr = "ntype: " + repr(self.__ntype)
return (layerRepr + "\n"
+ nameRepr + "\n"
+ ntypeRepr)
def __str__(self):
layerStr = "layer_list: " + str(self.__layer_list)
nameStr = "name_list: " + str(self.__name_list)
ntypeStr = "ntype: " + str(self.__ntype)
return (layerStr + "\n"
+ nameStr + "\n"
+ ntypeStr)
__len__
メソッドの実装
__len__
メソッドではPythonのビルドイン関数の一つであるlen
関数から呼ばれたときの動作を記述します。
LayerManager
クラスが持つ層の数を返すようにしておきましょう。
`__len__`の実装
def __len__(self):
"""
Pythonのビルドイン関数`len`から呼ばれたときの動作を記述。
種類別レイヤーの数の総和を返します。
"""
return int(np.sum(self.__ntype))
__getitem__
メソッドの実装
__getitem__
メソッドはPythonのリストやnumpy配列などで、インデックスなどを指定して要素を取得する際に呼び出されるメソッドです。
`__getitem__`の実装
def __getitem__(self, key):
"""
例えば
lm = LayerManager()
+----------------+
| (lmに要素を追加) |
+----------------+
x = lm[3].~~
のように、リストや配列の要素にアクセスされたときに呼ばれるので、
そのときの動作を記述。
sliceやstr, intでのアクセスのみ許可します。
"""
if isinstance(key, slice):
# keyがスライスならレイヤーのリストをsliceで参照する。
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
return self.__layer_list[key]
elif isinstance(key, str):
# keyが文字列なら各レイヤーの名前リストからインデックスを取得して、
# 該当するレイヤーのリストの要素を返す。
if key in self.__name_list:
index = self.__name_list.index(key)
return self.__layer_list[index]
else:
# keyが存在しない場合はKeyErrorを出す。
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
# keyが整数ならレイヤーのリストの該当要素を返す。
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
return self.__layer_list[key]
else:
raise KeyError(key, ": Undefined such key type.")
__setitem__
メソッドの実装
__setitem__
はDictionary
型などでよく用いられる、key
とvalue
をlm[key] = value
のようにしてセットするときに呼ばれるメソッドです。
要素の上書きのみ認めて、それ以外はエラーを出すようにします。
理由は、ネーミングルールを後ほど用いるため、ユーザに勝手なkey
を登録させたくないからです。
`__setitem__`の実装
def __setitem__(self, key, value):
"""
例えば
lm = LayerManager()
+----------------+
| (lmに要素を追加) |
+----------------+
lm[1] = x
のように、リストや配列の要素にアクセスされたときに呼ばれるので、
そのときの動作を記述。
要素の上書きのみ認め、新規要素の追加などは禁止します。
"""
value_type = ""
if isinstance(value, list):
# 右辺で指定された'value'が'list'なら
# 全ての要素が'BaseLayer'クラスかそれを継承していなければエラー。
if not np.all(
np.where(isinstance(value, BaseLayer), True, False)):
self.AssignError()
value_type = "list"
elif isinstance(value, BaseLayer):
# 右辺で指定された'value'が'BaseLayer'クラスか
# それを継承していない場合はエラー。
self.AssignError(type(value))
if value_type == "":
value_type = "BaseLayer"
if isinstance(key, slice):
# keyがスライスならレイヤーのリストの要素を上書きする。
# ただし'value_type'が'list'でなければエラー。
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
if value_type != "list":
self.AssignError(value_type)
self.__layer_list[key] = value
elif isinstance(key, str):
# keyが文字列なら各レイヤーの名前リストからインデックスを取得して、
# 該当するレイヤーのリストの要素を上書きする。
# ただし'value_type'が'BaseLayer'でなければエラー。
if value_type != "BaseLayer":
raise AssignError(value_type)
if key in self.__name_list:
index = self.__name_list.index(key)
self.__layer_list[index] = value
else:
# keyが存在しない場合はKeyErrorを出す。
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
# keyが整数ならレイヤーのリストの該当要素を上書きする。
# ただし'value_type'が'BaseLayer'でなければエラー。
# また、異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
if value_type != "BaseLayer":
raise AssignError(value_type)
self.__layer_list[key] = value
else:
raise KeyError(key, ": Undefined such key type.")
__delitem__
メソッドの実装
__delitem__
はdel lm[key]
などで呼び出されるメソッドです。こちらは指定の要素を削除するだけですね。
ただ、削除後の処理が少々面倒です。
`__delitem__`の実装
def __delitem__(self, key):
"""
例えば
lm = LayerManager()
+----------------+
| (lmに要素を追加) |
+----------------+
del lm[2]
のように、del文でリストや配列の要素にアクセスされたときに呼ばれるので、
そのときの動作を記述。
指定要素が存在すれば削除、さらにリネームを行います。
"""
if isinstance(key, slice):
# keyがスライスならそのまま指定の要素を削除
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
del self.__layer_list[slice]
del self.__name_list[slice]
elif isinstance(key, str):
# keyが文字列なら各レイヤーの名前リストからインデックスを取得して、
# 該当する要素を削除する。
if key in self.__name_list:
del self.__layer_list[index]
del self.__name_list[index]
else:
# keyが存在しない場合はKeyErrorを出す。
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
# keyが整数ならレイヤーのリストの該当要素を削除する。
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
del self.__layer_list[key]
else:
raise KeyError(key, ": Undefined such key type.")
# リネームする
self._rename()
蛇足
ちなみに_
(シングルアンダースコア)は__
(ダブルアンダースコア)よりも弱いprivate
属性を意味するものです。こちらは一応外部からでもクラス名._rename
のようにしてアクセスすることができますが、ダブルアンダースコアの方は例えばクラス名._モジュール名__メソッド名
のようにしないとアクセスできません。
また、クラスに属さないモジュール関数は_
によるprivate
指定があるとfrom モジュール名 import *
によってインポートすることはできません。ちゃんとfrom モジュール名 import _メソッド名
と呼ぶ必要があります。
ユーザ定義メソッドの実装
ここからはユーザ定義(つまりぼくたちで定義する)関数を実装していきます。ここでは基本的にList
型やDictionary
型の持つ有名どころなどを実装しようと思います。
_rename
メソッドの実装
とりあえず先ほどまでの実装で登場した_rename
メソッドを実装します。
これが必要となるのはどういう時かというと、
- 途中に要素が追加される
- 途中の要素が削除される
の二通りですね。
頑張ってロジックを考えればうまく変更する数とか減らせそうですが、めんどくさいので改めて最初から数え直すことにします。実装が簡単なので。メンテナンス性大事。
`_rename`メソッドの実装
def _rename(self):
"""
リスト操作によってネームリストのネーミングがルールに反するものになった場合に
改めてルールを満たすようにネーミングリストおよび各レイヤーの名前を変更する。
ネーミングルールは[レイヤーの種類][何番目か]とします。
レイヤーの種類はMiddleLayerならMiddle
OutputLayerならOutput
のように略します。
何番目かというのは種類別でカウントします。
また、ここで改めて__ntypeのカウントを行います。
"""
# 種類別レイヤーの数を初期化
self.__ntype = np.zeros(self.N_TYPE)
# 再カウントと各レイヤーのリネーム
for i in range(len(self)):
if "Middle" in self.__name_list[i]:
self.__ntype[self.MIDDLE] += 1
self.__name_list[i] = "Middle{}".format(
self.__ntype[self.MIDDLE])
self.__layer_list[i].name = "Middle{}".format(
self.__ntype[self.MIDDLE])
elif "Output" in self.__name_list[i]:
self.__ntype[self.OUTPUT] += 1
self.__name_list[i] = "Output{}".format(
self.__ntype[self.OUTPUT])
self.__layer_list[i].name = "Output{}".format(
self.__ntype[self.OUTPUT])
else:
raise UndefinedLayerType(self.__name_list[i])
append
メソッドの実装
リストに要素を追加するメソッドでお馴染みのappend
メソッドを実装します。最後尾に追加するのでそんなに複雑な処理はありませんね。
`append`メソッドの実装
def append(self, *, name="Middle", **kwds):
"""
リストに要素を追加するメソッドでお馴染みのappendメソッドの実装。
"""
if "prev" in kwds:
# 'prev'がキーワードに含まれている場合、
# 一つ前の層の要素数を指定していることになります。
# 基本的に最初のレイヤーを挿入する時を想定していますので、
# それ以外は基本的に自動で決定するため指定しません。
if len(self) != 0:
if kwds["prev"] != self.__layer_list[-1].n:
# 最後尾のユニット数と一致しなければエラー。
raise UnmatchUnitError(self.__layer_list[-1].n,
kwds["prev"])
else:
if len(self) == 0:
# 最初の層は必ず入力ユニットの数を指定する必要があります。
raise UnmatchUnitError("Input units", "Unspecified")
else:
# 最後尾のレイヤーのユニット数を'kwds'に追加
kwds["prev"] = self.__layer_list[-1].n
# レイヤーの種類を読み取り、ネーミングルールに則った名前に変更する
if name == "Middle" or name == "mid" or name == "m":
name = "Middle"
elif name == "Output" or name == "out" or name == "o":
name = "Output"
else:
raise UndefinedLayerError(name)
# レイヤーを追加する。
if name == "Middle":
# 種類別レイヤーをインクリメントして
self.__ntype[self.MIDDLE] += 1
# 名前に追加し
name += str(self.__ntype[self.MIDDLE])
# ネームリストに追加し
self.__name_list.append(name)
# 最後にレイヤーを生成してリストに追加します。
self.__layer_list.append(
MiddleLayer(name=name, **kwds))
elif name == "Output":
# こちらも同様です。
self.__ntype[self.OUTPUT] += 1
name += str(self.__ntype[self.OUTPUT])
self.__name_list.append(name)
self.__layer_list.append(
OutputLayer(name=name, **kwds))
# ここでelse文を描いていないのはネーミングルールに則った名前に変更する
# 段階で既に異常な'name'は省かれているからです。
extend
メソッドの実装
こちらはあまり馴染みがないかも?List
のextend
メソッドを実装します。
`extend`メソッドの説明
x = [1, 2, 3]
y = [4, 5, 6]
x.append(y)
print(x)
とすると、
[1, 2, 3, [4, 5, 6]]
のようになってしまうと思います。extend
メソッドを使うと
x = [1, 2, 3]
y = [4, 5, 6]
x.extend(y)
print(x)
の結果が
[1, 2, 3, 4, 5, 6]
となります。
`extend`メソッドの実装
def extend(self, lm):
"""
extendメソッドでは既にある別のレイヤーマネージャ'lm'の要素を
全て追加します。
"""
if not isinstance(lm, LayerManager):
# 'lm'のインスタンスがLayerManagerでなければエラー。
raise TypeError(type(lm), ": Unexpected type.")
if len(self) != 0:
if self.__layer_list[-1].n != lm[0].prev:
# 自分の最後尾のレイヤーのユニット数と
# 'lm'の最初のレイヤーの入力数が一致しない場合はエラー。
raise UnmatchUnitError(self.__layer_list[-1].n,
lm[0].prev)
# それぞれ'extend'メソッドで追加
self.__layer_list.extend(lm.layer_list)
self.__name_list.extend(lm.name_list)
# リネームする
self._rename()
insert
メソッドの実装
これもあんまり馴染みがないかもしれませんね。指定の位置に要素を追加するメソッドです。
`insert`メソッドの実装
def insert(self, prev_name, name="Middle", **kwds):
"""
insertメソッドでは、前のレイヤーの名前を指定しそのレイヤーと結合するように
要素を追加します。
"""
# 'prev_name'が存在しなければエラー。
if not prev_name in self.__name_list:
raise KeyError(prev_name, ": No such key.")
# 'prev'がキーワードに含まれている場合、
# 'prev_name'で指定されているレイヤーのユニット数と一致しなければエラー。
if "prev" in kwds:
if kwds["prev"] \
!= self.__layer_list[self.index(prev_name)].n:
raise UnmatchUnitError(
kwds["prev"],
self.__layer_list[self.index(prev_name)].n)
# 'n'がキーワードに含まれている場合、
if "n" in kwds:
# 'prev_name'が最後尾ではない場合は
if prev_name != self.__name_list[-1]:
# 次のレイヤーのユニット数と一致しなければエラー。
if kwds["n"] != self.__layer_list[
self.index(prev_name)+1].prev:
raise UnmatchUnitError(
kwds["n"],
self.__layer_list[self.index(prev_name)].prev)
# まだ何も要素がない場合は'append'メソッドを用いるようにエラーを出す。
if len(self) == 0:
raise RuntimeError(
"You have to use 'append' method instead.")
# 挿入場所のインデックスを取得
index = self.index(prev_name) + 1
# レイヤーの種類を読み取り、ネーミングルールに則った名前に変更する
if name == "Middle" or name == "mid" or name == "m":
name = "Middle"
elif name == "Output" or name == "out" or name == "o":
name = "Output"
else:
raise UndefinedLayerError(name)
# 要素を挿入する
# このとき、'name'はまだネーミングルールに則っていませんが、
# あとでリネームするので気にしないでOKです。
if "Middle" in name:
self.__layer_list.insert(index,
MiddleLayer(name=name, **kwds))
self.__name_list.insert(index, name)
elif "Output" in name:
self.__layer_list.insert(index,
OutputLayer(name=name, **kwds))
self.__name_list.insert(index, name)
# リネームする
self._rename()
extend_insert
メソッドの実装
こちらはオリジナルです。extend
メソッドとinsert
メソッドを組み合わせたような動作を行います。
`extend_insert`メソッドの実装
def extend_insert(self, prev_name, lm):
"""
こちらはオリジナル関数です。
extendメソッドとinsertメソッドを組み合わせたような動作をします。
簡単に説明すると、別のレイヤーマネージャをinsertする感じです。
"""
if not isinstance(lm, LayerManager):
# 'lm'のインスタンスがLayerManagerでなければエラー。
raise TypeError(type(lm), ": Unexpected type.")
# 'prev_name'が存在しなければエラー。
if not prev_name in self.__name_list:
raise KeyError(prev_name, ": No such key.")
# 指定場所の前後のレイヤーとlmの最初・最後のレイヤーのユニット数が
# それぞれ一致しなければエラー。
if len(self) != 0:
if self.__layer_list[self.index(prev_name)].n \
!= lm.layer_list[0].prev:
# 自分の指定場所のユニット数と'lm'の最初のユニット数が
# 一致しなければエラー。
raise UnmatchUnitError(
self.__layer_list[self.index(prev_name)].n,
lm.layer_list[0].prev)
if prev_name != self.__name_list[-1]:
# 'prev_name'が自分の最後尾のレイヤーでなく
if lm.layer_list[-1].n \
!= self.__layer_list[self.index(prev_name)+1].prev:
# 'lm'の最後尾のユニット数と自分の指定場所の次のレイヤーの
# 'prev'ユニット数と一致しなければエラー。
raise UnmatchUnitError(
lm.layer_list[-1].n,
self.__layer_list[self.index(prev_name)+1].prev)
else:
# 自分に何の要素もない場合は'extend'メソッドを使うようにエラーを出す。
raise RuntimeError(
"You have to use 'extend' method instead.")
# 挿入場所のインデックスを取得
index = self.index(prev_name) + 1
# 挿入場所以降の要素を'buf'に避難させてから一旦取り除き、
# extendメソッドを使って要素を追加
layer_buf = self.__layer_list[index:]
name_buf = self.__name_list[index:]
del self.__layer_list[index:]
del self.__name_list[index:]
self.extend(lm)
# 避難させていた要素を追加する
self.__layer_list.extend(layer_buf)
self.__name_list.extend(name_buf)
# リネームする
self._rename()
remove
メソッドの実装
remove
メソッドは指定の要素を削除するものですね。
こちらはもうdel文の実装で済ませてあるのでそれを呼ぶだけですね。
`remove`メソッドの実装
def remove(self, key):
"""
removeメソッドでは指定の名前の要素を削除します。
インデックスでの指定も許可します。
"""
# 既に実装している'del'文でOKです。
del self[key]
その他のメソッドの実装
上記以外の、あると便利かな〜というメソッドリストです。
便利メソッド実装
def index(self, target):
return self.__name_list.index(target)
def name(self, indices):
return self.__name_list[indices]
プロパティの実装
ここではプロパティを実装します。基本的にgetter
しか要らないので@property
でデコレートして実装します。
もしsetter
を追加したいとかなった場合は@プロパティ名.setter
デコレータを用いればOKですね。
プロパティの実装
@property
def layer_list(self):
return self.__layer_list
@property
def name_list(self):
return self.__name_list
@property
def ntype(self):
return self.__ntype
コード全文
これまでのレイヤーマネージャのコードを統合して全文として載せます。
もちろん今後の記事でメソッドなどを追加していきます。
layermanager.py
import numpy as np
class _TypeManager():
"""
層の種類に関するマネージャクラス
"""
N_TYPE = 2 # 層の種類数
MIDDLE = 0 # 中間層のナンバリング
OUTPUT = 1 # 出力層のナンバリング
class LayerManager(_TypeManager):
"""
層を管理するためのマネージャクラス
"""
def __init__(self):
self.__layer_list = [] # レイヤーのリスト
self.__name_list = [] # 各レイヤーの名前リスト
self.__ntype = np.zeros(self.N_TYPE, dtype=int) # 種類別レイヤーの数
def __repr__(self):
layerRepr= "layer_list: " + repr(self.__layer_list)
nameRepr = "name_list: " + repr(self.__name_list)
ntypeRepr = "ntype: " + repr(self.__ntype)
return (layerRepr + "\n"
+ nameRepr + "\n"
+ ntypeRepr)
def __str__(self):
layerStr = "layer_list: " + str(self.__layer_list)
nameStr = "name_list: " + str(self.__name_list)
ntypeStr = "ntype: " + str(self.__ntype)
return (layerStr + "\n"
+ nameStr + "\n"
+ ntypeStr)
def __len__(self):
"""
Pythonのビルドイン関数`len`から呼ばれたときの動作を記述。
種類別レイヤーの数の総和を返します。
"""
return int(np.sum(self.__ntype))
def __getitem__(self, key):
"""
例えば
lm = LayerManager()
+----------------+
| (lmに要素を追加) |
+----------------+
x = lm[3].~~
のように、リストや配列の要素にアクセスされたときに呼ばれるので、
そのときの動作を記述。
sliceやstr, intでのアクセスのみ許可します。
"""
if isinstance(key, slice):
# keyがスライスならレイヤーのリストをsliceで参照する。
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
return self.__layer_list[key]
elif isinstance(key, str):
# keyが文字列なら各レイヤーの名前リストからインデックスを取得して、
# 該当するレイヤーのリストの要素を返す。
if key in self.__name_list:
index = self.__name_list.index(key)
return self.__layer_list[index]
else:
# keyが存在しない場合はKeyErrorを出す。
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
# keyが整数ならレイヤーのリストの該当要素を返す。
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
return self.__layer_list[key]
else:
raise KeyError(key, ": Undefined such key type.")
def __setitem__(self, key, value):
"""
例えば
lm = LayerManager()
+----------------+
| (lmに要素を追加) |
+----------------+
lm[1] = x
のように、リストや配列の要素にアクセスされたときに呼ばれるので、
そのときの動作を記述。
要素の上書きのみ認め、新規要素の追加などは禁止します。
"""
value_type = ""
if isinstance(value, list):
# 右辺で指定された'value'が'list'なら
# 全ての要素が'BaseLayer'クラスかそれを継承していなければエラー。
if not np.all(
np.where(isinstance(value, BaseLayer), True, False)):
self.AssignError()
value_type = "list"
elif isinstance(value, BaseLayer):
# 右辺で指定された'value'が'BaseLayer'クラスか
# それを継承していない場合はエラー。
self.AssignError(type(value))
if value_type == "":
value_type = "BaseLayer"
if isinstance(key, slice):
# keyがスライスならレイヤーのリストの要素を上書きする。
# ただし'value_type'が'list'でなければエラー。
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
if value_type != "list":
self.AssignError(value_type)
self.__layer_list[key] = value
elif isinstance(key, str):
# keyが文字列なら各レイヤーの名前リストからインデックスを取得して、
# 該当するレイヤーのリストの要素を上書きする。
# ただし'value_type'が'BaseLayer'でなければエラー。
if value_type != "BaseLayer":
raise AssignError(value_type)
if key in self.__name_list:
index = self.__name_list.index(key)
self.__layer_list[index] = value
else:
# keyが存在しない場合はKeyErrorを出す。
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
# keyが整数ならレイヤーのリストの該当要素を上書きする。
# ただし'value_type'が'BaseLayer'でなければエラー。
# また、異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
if value_type != "BaseLayer":
raise AssignError(value_type)
self.__layer_list[key] = value
else:
raise KeyError(key, ": Undefined such key type.")
def __delitem__(self, key):
"""
例えば
lm = LayerManager()
+----------------+
| (lmに要素を追加) |
+----------------+
del lm[2]
のように、del文でリストや配列の要素にアクセスされたときに呼ばれるので、
そのときの動作を記述。
指定要素が存在すれば削除、さらにリネームを行います。
"""
if isinstance(key, slice):
# keyがスライスならそのまま指定の要素を削除
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
del self.__layer_list[slice]
del self.__name_list[slice]
elif isinstance(key, str):
# keyが文字列なら各レイヤーの名前リストからインデックスを取得して、
# 該当する要素を削除する。
if key in self.__name_list:
del self.__layer_list[index]
del self.__name_list[index]
else:
# keyが存在しない場合はKeyErrorを出す。
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
# keyが整数ならレイヤーのリストの該当要素を削除する。
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
del self.__layer_list[key]
else:
raise KeyError(key, ": Undefined such key type.")
# リネームする
self._rename()
def _rename(self):
"""
リスト操作によってネームリストのネーミングがルールに反するものになった場合に
改めてルールを満たすようにネーミングリストおよび各レイヤーの名前を変更する。
ネーミングルールは[レイヤーの種類][何番目か]とします。
レイヤーの種類はMiddleLayerならMiddle
OutputLayerならOutput
のように略します。
何番目かというのは種類別でカウントします。
また、ここで改めて__ntypeのカウントを行います。
"""
# 種類別レイヤーの数を初期化
self.__ntype = np.zeros(self.N_TYPE)
# 再カウントと各レイヤーのリネーム
for i in range(len(self)):
if "Middle" in self.__name_list[i]:
self.__ntype[self.MIDDLE] += 1
self.__name_list[i] = "Middle{}".format(
self.__ntype[self.MIDDLE])
self.__layer_list[i].name = "Middle{}".format(
self.__ntype[self.MIDDLE])
elif "Output" in self.__name_list[i]:
self.__ntype[self.OUTPUT] += 1
self.__name_list[i] = "Output{}".format(
self.__ntype[self.OUTPUT])
self.__layer_list[i].name = "Output{}".format(
self.__ntype[self.OUTPUT])
else:
raise UndefinedLayerType(self.__name_list[i])
def append(self, *, name="Middle", **kwds):
"""
リストに要素を追加するメソッドでお馴染みのappendメソッドの実装。
"""
if "prev" in kwds:
# 'prev'がキーワードに含まれている場合、
# 一つ前の層の要素数を指定していることになります。
# 基本的に最初のレイヤーを挿入する時を想定していますので、
# それ以外は基本的に自動で決定するため指定しません。
if len(self) != 0:
if kwds["prev"] != self.__layer_list[-1].n:
# 最後尾のユニット数と一致しなければエラー。
raise UnmatchUnitError(self.__layer_list[-1].n,
kwds["prev"])
else:
if len(self) == 0:
# 最初の層は必ず入力ユニットの数を指定する必要があります。
raise UnmatchUnitError("Input units", "Unspecified")
else:
# 最後尾のレイヤーのユニット数を'kwds'に追加
kwds["prev"] = self.__layer_list[-1].n
# レイヤーの種類を読み取り、ネーミングルールに則った名前に変更する
if name == "Middle" or name == "mid" or name == "m":
name = "Middle"
elif name == "Output" or name == "out" or name == "o":
name = "Output"
else:
raise UndefinedLayerError(name)
# レイヤーを追加する。
if name == "Middle":
# 種類別レイヤーをインクリメントして
self.__ntype[self.MIDDLE] += 1
# 名前に追加し
name += str(self.__ntype[self.MIDDLE])
# ネームリストに追加し
self.__name_list.append(name)
# 最後にレイヤーを生成してリストに追加します。
self.__layer_list.append(
MiddleLayer(name=name, **kwds))
elif name == "Output":
# こちらも同様です。
self.__ntype[self.OUTPUT] += 1
name += str(self.__ntype[self.OUTPUT])
self.__name_list.append(name)
self.__layer_list.append(
OutputLayer(name=name, **kwds))
# ここでelse文を描いていないのはネーミングルールに則った名前に変更する
# 段階で既に異常な'name'は省かれているからです。
def extend(self, lm):
"""
extendメソッドでは既にある別のレイヤーマネージャ'lm'の要素を
全て追加します。
"""
if not isinstance(lm, LayerManager):
# 'lm'のインスタンスがLayerManagerでなければエラー。
raise TypeError(type(lm), ": Unexpected type.")
if len(self) != 0:
if self.__layer_list[-1].n != lm[0].prev:
# 自分の最後尾のレイヤーのユニット数と
# 'lm'の最初のレイヤーの入力数が一致しない場合はエラー。
raise UnmatchUnitError(self.__layer_list[-1].n,
lm[0].prev)
# それぞれ'extend'メソッドで追加
self.__layer_list.extend(lm.layer_list)
self.__name_list.extend(lm.name_list)
# リネームする
self._rename()
def insert(self, prev_name, name="Middle", **kwds):
"""
insertメソッドでは、前のレイヤーの名前を指定しそのレイヤーと結合するように
要素を追加します。
"""
# 'prev_name'が存在しなければエラー。
if not prev_name in self.__name_list:
raise KeyError(prev_name, ": No such key.")
# 'prev'がキーワードに含まれている場合、
# 'prev_name'で指定されているレイヤーのユニット数と一致しなければエラー。
if "prev" in kwds:
if kwds["prev"] \
!= self.__layer_list[self.index(prev_name)].n:
raise UnmatchUnitError(
kwds["prev"],
self.__layer_list[self.index(prev_name)].n)
# 'n'がキーワードに含まれている場合、
if "n" in kwds:
# 'prev_name'が最後尾ではない場合は
if prev_name != self.__name_list[-1]:
# 次のレイヤーのユニット数と一致しなければエラー。
if kwds["n"] != self.__layer_list[
self.index(prev_name)+1].prev:
raise UnmatchUnitError(
kwds["n"],
self.__layer_list[self.index(prev_name)].prev)
# まだ何も要素がない場合は'append'メソッドを用いるようにエラーを出す。
if len(self) == 0:
raise RuntimeError(
"You have to use 'append' method instead.")
# 挿入場所のインデックスを取得
index = self.index(prev_name) + 1
# レイヤーの種類を読み取り、ネーミングルールに則った名前に変更する
if name == "Middle" or name == "mid" or name == "m":
name = "Middle"
elif name == "Output" or name == "out" or name == "o":
name = "Output"
else:
raise UndefinedLayerError(name)
# 要素を挿入する
# このとき、'name'はまだネーミングルールに則っていませんが、
# あとでリネームするので気にしないでOKです。
if "Middle" in name:
self.__layer_list.insert(index,
MiddleLayer(name=name, **kwds))
self.__name_list.insert(index, name)
elif "Output" in name:
self.__layer_list.insert(index,
OutputLayer(name=name, **kwds))
self.__name_list.insert(index, name)
# リネームする
self._rename()
def extend_insert(self, prev_name, lm):
"""
こちらはオリジナル関数です。
extendメソッドとinsertメソッドを組み合わせたような動作をします。
簡単に説明すると、別のレイヤーマネージャをinsertする感じです。
"""
if not isinstance(lm, LayerManager):
# 'lm'のインスタンスがLayerManagerでなければエラー。
raise TypeError(type(lm), ": Unexpected type.")
# 'prev_name'が存在しなければエラー。
if not prev_name in self.__name_list:
raise KeyError(prev_name, ": No such key.")
# 指定場所の前後のレイヤーとlmの最初・最後のレイヤーのユニット数が
# それぞれ一致しなければエラー。
if len(self) != 0:
if self.__layer_list[self.index(prev_name)].n \
!= lm.layer_list[0].prev:
# 自分の指定場所のユニット数と'lm'の最初のユニット数が
# 一致しなければエラー。
raise UnmatchUnitError(
self.__layer_list[self.index(prev_name)].n,
lm.layer_list[0].prev)
if prev_name != self.__name_list[-1]:
# 'prev_name'が自分の最後尾のレイヤーでなく
if lm.layer_list[-1].n \
!= self.__layer_list[self.index(prev_name)+1].prev:
# 'lm'の最後尾のユニット数と自分の指定場所の次のレイヤーの
# 'prev'ユニット数と一致しなければエラー。
raise UnmatchUnitError(
lm.layer_list[-1].n,
self.__layer_list[self.index(prev_name)+1].prev)
else:
# 自分に何の要素もない場合は'extend'メソッドを使うようにエラーを出す。
raise RuntimeError(
"You have to use 'extend' method instead.")
# 挿入場所のインデックスを取得
index = self.index(prev_name) + 1
# 挿入場所以降の要素を'buf'に避難させてから一旦取り除き、
# extendメソッドを使って要素を追加
layer_buf = self.__layer_list[index:]
name_buf = self.__name_list[index:]
del self.__layer_list[index:]
del self.__name_list[index:]
self.extend(lm)
# 避難させていた要素を追加する
self.__layer_list.extend(layer_buf)
self.__name_list.extend(name_buf)
# リネームする
self._rename()
def remove(self, key):
"""
removeメソッドでは指定の名前の要素を削除します。
インデックスでの指定も許可します。
"""
# 既に実装している'del'文でOKです。
del self[key]
def index(self, target):
return self.__name_list.index(target)
def name(self, indices):
return self.__name_list[indices]
@property
def layer_list(self):
return self.__layer_list
@property
def name_list(self):
return self.__name_list
@property
def ntype(self):
return self.__ntype
レイヤーエラーのコード準備
_layererror
モジュールの紹介です。とりあえずここまでの実装で出てきたエラーだけ実装してあります。
これも追加していく可能性があります。
_layererror.py
class LayerManagerError(Exception):
"""レイヤーモジュールにおけるユーザ定義エラーのベースクラス"""
pass
class AssignError(LayerManagerError):
def __init__(self, value=None):
if not value is None:
self.value = value
self.message = (str(value)
+ ": Assigning that value is prohibited.")
else:
self.value = None
self.message = "Assigning that value is prohibited."
def __str__(self):
return self.message
class UnmatchUnitError(LayerManagerError):
def __init__(self, prev, n):
self.prev = prev
self.n = n
self.message = "Unmatch units: {} and {}.".format(prev, n)
def __str__(self):
return self.message
class UndefinedLayerError(LayerManagerError):
def __init__(self, type_name):
self.type = type_name
self.message = str(type_name) + ": Undefined layer type."
def __str__(self):
return self.message
最初の
class LayerManagerError(Exception):
"""レイヤーモジュールにおけるユーザ定義エラーのベースクラス"""
pass
は、レイヤーに関する例外のキャッチを
try:
raise AssignError()
except LayerManagerError:
print("catch LayerManagerError")
のように、これを継承するエラーを一括でキャッチできるようにするためのものです。もちろん個別にエラーキャッチすることもできます。
また、__str__
メソッドを定義して初めてエラー文を出力できます。
おわりに
ということで、ここではレイヤーオブジェクトの雛形と、それを扱うためのマネージャ、およびエラー制御のためのモジュールを作成しました。
次回以降の記事でどんどん実装の中身を追加していきます。
掲載したコード一覧
baselayer.py [ここ](#レイヤーモジュールのコード準備)
import numpy as np
class BaseLayer():
"""
全ての元となるレイヤークラス
中間層と出力層で共通する処理を記述する。
"""
def __init__(self):
pass
def forward(self):
"""
順伝播の実装
"""
pass
def backward(self):
"""
逆伝播の実装
"""
pass
def update(self):
"""
パラメータ学習の実装
"""
pass
middlelayer.py [ここ](#レイヤーモジュールのコード準備)
import numpy as np
#import .baselayer import BaseLayer
class MiddleLayer(BaseLayer):
"""
中間層クラス
入力層も実装上は中間層の一つとして取り扱います。
"""
pass
outputlayer.py [ここ](#レイヤーモジュールのコード準備)
import numpy as np
#from .baselayer import BaseLayer
class OutputLayer(BaseLayer):
"""
出力層クラス
"""
pass
layermanager.py [ここ](#コード全文)
import numpy as np
#from .layer import *
class _TypeManager():
"""
層の種類に関するマネージャクラス
"""
N_TYPE = 2 # 層の種類数
MIDDLE = 0 # 中間層のナンバリング
OUTPUT = 1 # 出力層のナンバリング
class LayerManager(_TypeManager):
"""
層を管理するためのマネージャクラス
"""
def __init__(self):
self.__layer_list = [] # レイヤーのリスト
self.__name_list = [] # 各レイヤーの名前リスト
self.__ntype = np.zeros(self.N_TYPE) # 種類別レイヤーの数
def __repr__(self):
layerRepr= "layer_list: " + repr(self.__layer_list)
nameRepr = "name_list: " + repr(self.__name_list)
ntypeRepr = "ntype: " + repr(self.__ntype)
return (layerRepr + "\n"
+ nameRepr + "\n"
+ ntypeRepr)
def __str__(self):
layerStr = "layer_list: " + str(self.__layer_list)
nameStr = "name_list: " + str(self.__name_list)
ntypeStr = "ntype: " + str(self.__ntype)
return (layerStr + "\n"
+ nameStr + "\n"
+ ntypeStr)
def __len__(self):
"""
Pythonのビルドイン関数`len`から呼ばれたときの動作を記述。
種類別レイヤーの数の総和を返します。
"""
return np.sum(self.__ntype)
def __getitem__(self, key):
"""
例えば
lm = LayerManager()
+----------------+
| (lmに要素を追加) |
+----------------+
x = lm[3].~~
のように、リストや配列の要素にアクセスされたときに呼ばれるので、
そのときの動作を記述。
sliceやstr, intでのアクセスのみ許可します。
"""
if isinstance(key, slice):
# keyがスライスならレイヤーのリストをsliceで参照する。
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
return self.__layer_list[key]
elif isinstance(key, str):
# keyが文字列なら各レイヤーの名前リストからインデックスを取得して、
# 該当するレイヤーのリストの要素を返す。
if key in self.__name_list:
index = self.__name_list.index(key)
return self.__layer_list[index]
else:
# keyが存在しない場合はKeyErrorを出す。
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
# keyが整数ならレイヤーのリストの該当要素を返す。
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
return self.__layer_list[key]
else:
raise KeyError(key, ": Undefined such key type.")
def __setitem__(self, key, value):
"""
例えば
lm = LayerManager()
+----------------+
| (lmに要素を追加) |
+----------------+
lm[1] = x
のように、リストや配列の要素にアクセスされたときに呼ばれるので、
そのときの動作を記述。
要素の上書きのみ認め、新規要素の追加などは禁止します。
"""
value_type = ""
if isinstance(value, list):
# 右辺で指定された'value'が'list'なら
# 全ての要素が'BaseLayer'クラスかそれを継承していなければエラー。
if not np.all(
np.where(isinstance(value, BaseLayer), True, False)):
self.AssignError()
value_type = "list"
elif isinstance(value, BaseLayer):
# 右辺で指定された'value'が'BaseLayer'クラスか
# それを継承していない場合はエラー。
self.AssignError(type(value))
if value_type == "":
value_type = "BaseLayer"
if isinstance(key, slice):
# keyがスライスならレイヤーのリストの要素を上書きする。
# ただし'value_type'が'list'でなければエラー。
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
if value_type != "list":
self.AssignError(value_type)
self.__layer_list[key] = value
elif isinstance(key, str):
# keyが文字列なら各レイヤーの名前リストからインデックスを取得して、
# 該当するレイヤーのリストの要素を上書きする。
# ただし'value_type'が'BaseLayer'でなければエラー。
if value_type != "BaseLayer":
raise AssignError(value_type)
if key in self.__name_list:
index = self.__name_list.index(key)
self.__layer_list[index] = value
else:
# keyが存在しない場合はKeyErrorを出す。
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
# keyが整数ならレイヤーのリストの該当要素を上書きする。
# ただし'value_type'が'BaseLayer'でなければエラー。
# また、異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
if value_type != "BaseLayer":
raise AssignError(value_type)
self.__layer_list[key] = value
else:
raise KeyError(key, ": Undefined such key type.")
def __delitem__(self, key):
"""
例えば
lm = LayerManager()
+----------------+
| (lmに要素を追加) |
+----------------+
del lm[2]
のように、del文でリストや配列の要素にアクセスされたときに呼ばれるので、
そのときの動作を記述。
指定要素が存在すれば削除、さらにリネームを行います。
"""
if isinstance(key, slice):
# keyがスライスならそのまま指定の要素を削除
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
del self.__layer_list[slice]
del self.__name_list[slice]
elif isinstance(key, str):
# keyが文字列なら各レイヤーの名前リストからインデックスを取得して、
# 該当する要素を削除する。
if key in self.__name_list:
del self.__layer_list[index]
del self.__name_list[index]
else:
# keyが存在しない場合はKeyErrorを出す。
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
# keyが整数ならレイヤーのリストの該当要素を削除する。
# 異常な値(Index out of rangeなど)が入力されたら
# Pythonがエラーを出してくれます。
del self.__layer_list[key]
else:
raise KeyError(key, ": Undefined such key type.")
# リネームする
self._rename()
def _rename(self):
"""
リスト操作によってネームリストのネーミングがルールに反するものになった場合に
改めてルールを満たすようにネーミングリストおよび各レイヤーの名前を変更する。
ネーミングルールは[レイヤーの種類][何番目か]とします。
レイヤーの種類はMiddleLayerならMiddle
OutputLayerならOutput
のように略します。
何番目かというのは種類別でカウントします。
また、ここで改めて__ntypeのカウントを行います。
"""
# 種類別レイヤーの数を初期化
self.__ntype = np.zeros(self.N_TYPE)
# 再カウントと各レイヤーのリネーム
for i in range(len(self)):
if "Middle" in self.__name_list[i]:
self.__ntype[self.MIDDLE] += 1
self.__name_list[i] = "Middle{}".format(
self.__ntype[self.MIDDLE])
self.__layer_list[i].name = "Middle{}".format(
self.__ntype[self.MIDDLE])
elif "Output" in self.__name_list[i]:
self.__ntype[self.OUTPUT] += 1
self.__name_list[i] = "Output{}".format(
self.__ntype[self.OUTPUT])
self.__layer_list[i].name = "Output{}".format(
self.__ntype[self.OUTPUT])
else:
raise UndefinedLayerType(self.__name_list[i])
def append(self, *, name="Middle", **kwds):
"""
リストに要素を追加するメソッドでお馴染みのappendメソッドの実装。
"""
if "prev" in kwds:
# 'prev'がキーワードに含まれている場合、
# 一つ前の層の要素数を指定していることになります。
# 基本的に最初のレイヤーを挿入する時を想定していますので、
# それ以外は基本的に自動で決定するため指定しません。
if len(self) != 0:
if kwds["prev"] != self.__layer_list[-1].n:
# 最後尾のユニット数と一致しなければエラー。
raise UnmatchUnitError(self.__layer_list[-1].n,
kwds["prev"])
else:
if len(self) == 0:
# 最初の層は必ず入力ユニットの数を指定する必要があります。
raise UnmatchUnitError("Input units", "Unspecified")
else:
# 最後尾のレイヤーのユニット数を'kwds'に追加
kwds["prev"] = self.__layer_list[-1].n
# レイヤーの種類を読み取り、ネーミングルールに則った名前に変更する
if name == "mid" or "m":
name = "Middle"
elif name == "out" or "o":
name = "Output"
else:
raise UndefinedLayerError(name)
# レイヤーを追加する。
if name == "Middle":
# 種類別レイヤーをインクリメントして
self.__ntype[self.MIDDLE] += 1
# 名前に追加し
name += str(self.__ntype[self.MIDDLE])
# ネームリストに追加し
self.__name_list.append(name)
# 最後にレイヤーを生成してリストに追加します。
self.__layer_list.append(
MiddleLayer(name=name, **kwds))
elif name == "Output":
# こちらも同様です。
self.__ntype[self.OUTPUT] += 1
name += str(self.__ntype[self.OUTPUT])
self.__name_list.append(name)
self.__layer_list.append(
OutputLayer(name=name, **kwds))
# ここでelse文を描いていないのはネーミングルールに則った名前に変更する
# 段階で既に異常な'name'は省かれているからです。
def extend(self, lm):
"""
extendメソッドでは既にある別のレイヤーマネージャ'lm'の要素を
全て追加します。
"""
if not isinstance(lm, LayerManager):
# 'lm'のインスタンスがLayerManagerでなければエラー。
raise TypeError(type(lm), ": Unexpected type.")
if len(self) != 0:
if self.__layer_list[-1].n != lm[0].prev:
# 自分の最後尾のレイヤーのユニット数と
# 'lm'の最初のレイヤーの入力数が一致しない場合はエラー。
raise UnmatchUnitError(self.__layer_list[-1].n,
lm[0].prev)
# それぞれ'extend'メソッドで追加
self.__layer_list.extend(lm.layer_list)
self.__name_list.extend(lm.name_list)
# リネームする
self._rename()
def insert(self, prev_name, name="Middle", **kwds):
"""
insertメソッドでは、前のレイヤーの名前を指定しそのレイヤーと結合するように
要素を追加します。
"""
# 'prev_name'が存在しなければエラー。
if not prev_name in self.__name_list:
raise KeyError(prev_name, ": No such key.")
# 'prev'がキーワードに含まれている場合、
# 'prev_name'で指定されているレイヤーのユニット数と一致しなければエラー。
if "prev" in kwds:
if kwds["prev"] \
!= self.__layer_list[self.index(prev_name)].n:
raise UnmatchUnitError(
kwds["prev"],
self.__layer_list[self.index(prev_name)].n)
# 'n'がキーワードに含まれている場合、
if "n" in kwds:
# 'prev_name'が最後尾ではない場合は
if prev_name != self.__name_list[-1]:
# 次のレイヤーのユニット数と一致しなければエラー。
if kwds["n"] != self.__layer_list[
self.index(prev_name)+1].prev:
raise UnmatchUnitError(
kwds["n"],
self.__layer_list[self.index(prev_name)].prev)
# まだ何も要素がない場合は'append'メソッドを用いるようにエラーを出す。
if len(self) == 0:
raise RuntimeError(
"You have to use 'append' method instead.")
# 挿入場所のインデックスを取得
index = self.index(prev_name) + 1
# レイヤーの種類を読み取り、ネーミングルールに則った名前に変更する
if name == "mid" or "m":
name = "Middle"
elif name == "out" or "o":
name = "Output"
else:
raise UndefinedLayerError(name)
# 要素を挿入する
# このとき、'name'はまだネーミングルールに則っていませんが、
# あとでリネームするので気にしないでOKです。
if "Middle" in name:
self.__layer_list.insert(index,
MiddleLayer(name=name, **kwds))
self.__name_list.insert(index, name)
elif "Output" in name:
self.__layer_list.insert(index,
OutputLayer(name=name, **kwds))
self.__name_list.insert(index, name)
# リネームする
self._rename()
def extend_insert(self, prev_name, lm):
"""
こちらはオリジナル関数です。
extendメソッドとinsertメソッドを組み合わせたような動作をします。
簡単に説明すると、別のレイヤーマネージャをinsertする感じです。
"""
if not isinstance(lm, LayerManager):
# 'lm'のインスタンスがLayerManagerでなければエラー。
raise TypeError(type(lm), ": Unexpected type.")
# 'prev_name'が存在しなければエラー。
if not prev_name in self.__name_list:
raise KeyError(prev_name, ": No such key.")
# 指定場所の前後のレイヤーとlmの最初・最後のレイヤーのユニット数が
# それぞれ一致しなければエラー。
if len(self) != 0:
if self.__layer_list[self.index(prev_name)].n \
!= lm.layer_list[0].prev:
# 自分の指定場所のユニット数と'lm'の最初のユニット数が
# 一致しなければエラー。
raise UnmatchUnitError(
self.__layer_list[self.index(prev_name)].n,
lm.layer_list[0].prev)
if prev_name != self.__name_list[-1]:
# 'prev_name'が自分の最後尾のレイヤーでなく
if lm.layer_list[-1].n \
!= self.__layer_list[self.index(prev_name)+1].prev:
# 'lm'の最後尾のユニット数と自分の指定場所の次のレイヤーの
# 'prev'ユニット数と一致しなければエラー。
raise UnmatchUnitError(
lm.layer_list[-1].n,
self.__layer_list[self.index(prev_name)+1].prev)
else:
# 自分に何の要素もない場合は'extend'メソッドを使うようにエラーを出す。
raise RuntimeError(
"You have to use 'extend' method instead.")
# 挿入場所のインデックスを取得
index = self.index(prev_name) + 1
# 挿入場所以降の要素を'buf'に避難させてから一旦取り除き、
# extendメソッドを使って要素を追加
layer_buf = self.__layer_list[index:]
name_buf = self.__name_list[index:]
del self.__layer_list[index:]
del self.__name_list[index:]
self.extend(lm)
# 避難させていた要素を追加する
self.__layer_list.extend(layer_buf)
self.__name_list.extend(name_buf)
# リネームする
self._rename()
def remove(self, key):
"""
removeメソッドでは指定の名前の要素を削除します。
インデックスでの指定も許可します。
"""
# 既に実装している'del'文でOKです。
del self[key]
def index(self, target):
return self.__name_list.index(target)
def name(self, indices):
return self.__name_list[indices]
@property
def layer_list(self):
return self.__layer_list
@property
def name_list(self):
return self.__name_list
@property
def ntype(self):
return self.__ntype
_layererror.py [ここ](#レイヤーエラーのコード準備)
class LayerManagerError(Exception):
"""レイヤーモジュールにおけるユーザ定義エラーのベースクラス"""
pass
class AssignError(LayerManagerError):
def __init__(self, value=None):
if not value is None:
self.value = value
self.message = (str(value)
+ ": Assigning that value is prohibited.")
else:
self.value = None
self.message = "Assigning that value is prohibited."
def __str__(self):
return self.message
class UnmatchUnitError(LayerManagerError):
def __init__(self, prev, n):
self.prev = prev
self.n = n
self.message = "Unmatch units: {} and {}.".format(prev, n)
def __str__(self):
return self.message
class UndefinedLayerError(LayerManagerError):
def __init__(self, type_name):
self.type = type_name
self.message = str(type_name) + ": Undefined layer type."
def __str__(self):
return self.message