🗂️ Group¶
mujou.run が走らせるルートは 1 つ。複数のネットワークを 1 つの実行で走らせるには、mujou.Group に並べる。このページでは書き方、パスの付き方、別々の実行に分けるべき場面を説明する。
パス¶
ノードはパスで呼ぶ。パスはルートからの属性名を . でつないだもの。
class Stack(mujou.Network):
def __init__(self) -> None:
super().__init__()
self.l1 = Hidden(784, 256)
self.l2 = Hidden(256, 256)
self.l1.next.connect(self.l2.prev)
self.prev, self.next = self.l1.prev, self.l2.next
class Classifier(mujou.Network):
def __init__(self) -> None:
super().__init__()
self.body = Stack() # ネットワークの中のネットワーク
self.head = Head()
self.body.next.connect(self.head.prev) # body.next の実体は l2.next
self.prev = self.body.prev # 子が公開した Port を、さらに公開する
Classifier のノードのパスは "body.l1"、"body.l2"、"head"。実行は属性を代入の順に読むので、それが木の順になる。ルートとして渡したノード自身のパスは ""。
パスは状態の木のキーになる。そのためネットワークのパスは走らせ方によらず同じで、属性名を変えると、変える前に保存した状態は復元できなくなる。
Group¶
# By keyword: two networks connected before they are grouped.
enc, dec = Stage(2.0), Stage(3.0)
enc.next.connect(dec.prev) # the ports a network exposes, as for nodes
x = torch.tensor([1.0, -1.0])
with mujou.run(Group(enc=enc, dec=dec), inputs={enc.prev: x}) as run:
# x takes a few updates to pass through the nodes, and how many fit in a given
# time depends on how the threads get scheduled: follow the output until y
# arrives (TimeoutError after 5 s).
# The ports are those of the networks given.
y, _ = run.wait_for(dec.next, lambda y: torch.equal(y, 6 * x), timeout=5.0)
nodes = list(run.state_dict()["nodes"])
print(f"keyword: nodes {nodes}, y = {y}")
assert nodes == ["enc.scale", "dec.scale"]
assert torch.equal(y, 6 * x)
# Two independent networks in one run.
first, second = Stage(2.0), Stage(3.0)
both = Group(first=first, second=second)
with mujou.run(both, inputs={first.prev: x, second.prev: x}) as run:
y1, _ = run.wait_for(first.next, lambda y: torch.equal(y, 2 * x), timeout=5.0)
y2, _ = run.wait_for(second.next, lambda y: torch.equal(y, 3 * x), timeout=5.0)
nodes = list(run.state_dict()["nodes"])
print(f"side by side: nodes {nodes}, y1 = {y1}, y2 = {y2}")
assert nodes == ["first.scale", "second.scale"]
assert torch.equal(y1, 2 * x) and torch.equal(y2, 3 * x)
Group(enc=a, dec=b) は各ネットワークをキーワードの下に置く。
| ルート | a の中のノード l1 のパス |
|---|---|
a |
"l1" |
Group(enc=a, dec=b) |
"enc.l1" |
node(ノード 1 つだけ) |
"" |
Group(n=node) |
"n" |
Group 自体は何もつながない。ネットワークは束ねる前か後につなぎ、入出力にはネットワークの Port をそのまま使う(enc.prev、dec.next)。状態を復元するときは、保存時と同じキーワードで束ねる。
ノードは 1 回だけ走る(ノードが守る規則)。単独で走らせたネットワークは後から束ねられないので、最初からまとめて走らせるか、新しいインスタンスを作る。
1 つの実行か、複数か¶
束ねたネットワークは 1 つの実行になり、1 本のタイマー、1 つの状態の木、1 つの終了を共有する。
| 項目 | 1 つの実行に束ねたとき | 別々の実行で、それぞれに Timer を渡したとき |
|---|---|---|
| つなぐこと | できる | できない。別の実行の Port はつなげない |
| 状態の取得 | 全ノードを止め、同じ時刻の木を 1 つ取得 | 実行ごとに 1 つ。止まるのはその実行だけ |
| 時間の速さ | 全ノードで同じ | 実行ごとに変えられる |
| 例外 | 1 つのノードの例外で全ネットワークが終了 | 例外を投げたノードの実行だけが終了 |
一方の状態を取得するときにもう一方を止めたくない、一方だけ速めたり遅くしたりしたい、一方が止まってももう一方は動かし続けたい。そういうときは別々の実行にし、それぞれに mujou.time.Timer() を渡す。
timer を渡さない実行はグローバルタイマーを共有する
timer を渡さない実行はどれも mujou.time.global_timer で走るので、1 つの状態を取得するとほかも止まる。止め合いたくないなら、それぞれに Timer を渡す(時間)。