TIM Labs

2016年10月アーカイブ

from_zero_deep_learning (283x400).jpgゼロから作るDeep Learning
―Pythonで学ぶディープラーニングの理論と実装

斎藤 康毅 著

A5版、320ページ
2016/9/24 発売
3400円(本体)
オライリー・ジャパン
ISBN-13: 978-487311-758-4

GitHub:oreilly-japan/deep-learning-from-scratch



2016年になって、AIブーム、Deep Learning ブームに乗じて多数の解説書が出た。

もちろん、その多くは入門書だったり、あれこれ出来るようになったことを紹介するだけの本が大多数で、読んでもDeep Learning についてDeep に理解できるようにはならない本が殆んどであった。

しかし、この本は違うのだ。ゼロから作るという枕言葉通り、Deep Learningのプログラムを、既成のライブラリなどを使うのではなくゼロから、スクラッチで書き起こすのをきちんと体験できる本である。
Deep Learningで何かやってさっさと成果を出したいという短絡的思考には役に立たない本だが、Deep Learningの仕組みをソースコードレベルでちゃんと理解してみたいという人には最適の本である。

書き方は非常に丁寧なので、きちんと読みながらPythonのプログラムを動かしていけば、Deep Learning とはこういうことかと納得できる。必要な知識は、大学2年程度の数学とプログラミング能力があれば、何も困らないはずで、大学の理工系3年以上でこの本の前半部分が難しい場合は、相当な勉強不足である。

1章 Python入門
本書を読むのに必要な最低限の説明がしてある。Pythonについては、何か別の本などでもう少し詳しく勉強しておいたほうが良いと思う。

2章 パーセプトロン
ニューラルネットワークの元になったパーセプトロンについての丁寧な解説がある。パーセプトロンで何ができるか説明してあるが、何ができないかも説明してある。

3章 ニューラルネットワーク
ここから本題が始まる。
ニューロン、神経細胞の働きをどうコンピュータで実現するか、そしてそれを多層化すると何が出来るかが説明されている。

4章 ニューラルネットワーク
第3章で多層のニューラルネットを作ったのだが、それに学習させるとはどういうことかの説明がしてある。実際にデータを与えると、どういう風に学習されていくかが、多数のデータ、図、式、プログラムを示しながら説明している。大量のパラメータの中に学習させるのだが、各パラメータを微小変化させたとき、結果がどう変わるかを求めて、全パラメータを少しずつ良くなるはずの方向に変化させる。つまり、数値偏微分で勾配を求めて処理するのである。

一応、Deep Learning の基礎の基礎は第4章までで終わる。しかし、このままでは、実用にならないほど超低速学習しかできない。

5章 誤差逆伝播法
高速に学習させる方法の説明がある。単に「逆伝播」ということが多いと思う。
数値偏微分は簡単なのだが無駄の塊みたいな計算をするので、効率の良い勾配の求め方を延々と説明してある。本書では、計算グラフを用いる方法で説明してある。

ここまで読み進むと、実用になる速度、といっても相当遅いのだが、一通りのDeep Learning の勉強ができたことになる。

6章 学習
学習も、やみくもにやってもダメで、上手く調整しないと、良い結果がでないという普通のことを説明しているのだが、この章あたりから説明のペースが上がるので要注意。

7章 畳み込みニューラルネットワーク
画像処理をやっていた人にはフィルタ処理と言えば分かると思う。この処理は画像あるいは画像的なデータに対してかなり有効になる。
小さいサイズの配列(フィルタと呼ぶ)を用意し、入力画像の上をフィルタを動かしながら画像データとフィルタとの演算を行っていく(この説明だと、分かる人にだけ分かる説明だな)。詳しくは、本書を読んでほしい。
畳み込みもとても重要な技術なのだが、本書の前半と比較すると、丁寧さが足りない。

8章 ディープラーニング
過去の歴史や、様々なディープラーニングの紹介があるが、かなりのハイペースで説明があり、きちんと理解するよりも、どんなものがあるのかだけをざっと知っておく程度の読み方になろう。

ということで、本書は原理をちゃんと理解することだけを説明している本である。
前半はとても丁寧だったのだが、後半はかなりハイピッチで丁寧さのギャップは激しい。
後半も前半並みの丁寧さで書いて欲しいところだが、ページ数が激増するとか、出版時期がどんどん遅くなるとかあって、ビジネス的に成り立たなくなる可能性がある。なので、この丁寧さあたりが一番妥当なところであろうか。

Deep Learning をきちんと勉強したければ、本書から始めるのが現時点ではベストであろう。

Sierpinskiの三角形を乱数フラクタルで描いた。でも、これは2次元である。今回は、3次元に拡張しよう。

前回のプログラムから3次元へは自然に拡張できる。
3次元に拡張したので、三角形が四角錐になる。ここでは、各辺の長さが同じの正四面体になるように、(0,0,0),(0,1,1),(1,0,1),(1,1,0)の4点を頂点として与えた。

まず、プログラムを示す。
num = input("How many points: ");
V = [ 0,0,0; 0,1,1; 1,0,1; 1,1,0 ];

P = [0,0,0];
X(1) = P(1);
Y(1) = P(2);
Z(1) = P(3);
for i = 2:num
    Q = V(randi(4),:);
    P = (P + Q)/2;
    X(i) = P(1);
    Y(i) = P(2);
    Z(i) = P(3);
endfor

plot3(X,Y,Z,".")

title( sprintf("Sierpinski pyramid, num=%d",num) )

1万点で描かせると、こういう風になる。
sierpinski100003d-1.png
これだと、一辺1の正方形に点がランダムに散らばっているだけに見える。
でも、ちょっと視点を移動すると、点が立体的に散らばっていることが分かる。

sierpinski100003d-2.png
さらに視点を移動していくと、Sierpinskiの三角形が見えてくる。

sierpinski100003d-3.png
sierpinski100003d-4.png
3次元図形は、結果を画像で見ても全然面白くない。マウスをグリグリすることで、見る方向がどんどん変わり、3次元を体感することがとても重要だ。ぜひ、Octave なり、MatLabを使ってみよう。

さて、何枚もの正四面体の図を示した。なので、もう一辺の長さが1の正四面体の面積の計算は簡単にできるようになったと思う。
個々で示した図には、横着(エレガント)な計算方法が示されている。この性質を利用した正四面体の体積計算は中学入試で出てくることもある。正四面体の底面積求めて高さを求めて体積を求めるとうい面倒なことは止めて、暗算可能な方法で教えよう。
詳しいことはネットにいっぱい説明があるので、自分で探そう。
前回、次の点の座標を求める関数NextPointを紹介したが、次の点の座標が式で示されていたので、何をやっているのか分かりにくかったと思う。

今回は、それを図で説明しよう。というか、図を見れば一目瞭然、とても簡単なことをしているだけだ。

sierpinskimidp.png最初に与えられた三角形の頂点をV1,V2,V3とする。今は、V1(0,0), V2(2,0), V3(1,1)となっている。
点Pが与えられたとき、次のP点を求めるのであるが,PとV1,V2,V3の中点をp1,p2,p3としたとき、p1,p2,p3のいずれか1つを同じ確率で次の点にすることを、前回は分かり難いプログラムで示した。わかり難くなっていたのは、参考にした本の式をそのまま利用したからである。

このように点列を次々に作っていき、点を順番に結ぶと、次の点は元の三角形のいずれかの頂点に向かって1/2だけ進んだ点になる。
これが前回示した図の意味である。丸と線を追いかけると良く分かるだろう。

sierpinskistep.png 原理がとても分かりやすくなったので、プログラムも当然分かりやすくなる。

num = input("How many points: ");
V = [ 0, 0; 2, 0; 1, 1 ];

P = [0,0];
X(1) = P(1)
Y(1) = P(2)
for i = 2:num
    Q = V(randi(3),:);
    P = (P + Q)/2;
    X(i) = P(1);
    Y(i) = P(2);
endfor

plot(X,Y,".b")

title( sprintf("Sierpinski triangle, num=%d",num) )
2次元図形の話を延々としてきたので飽きてきた。次回は3次元に拡張してみよう。
最初に示したランダムに点を打つのを詳しく調べるために、丸を大きくし、順番に繋いでみた。

sierpinskistep.png
>> sierpinski2d_1
How many points: 10
(0.500000,0.500000)
(0.750000,0.750000)
(1.375000,0.375000)
(1.187500,0.687500)
(0.593750,0.343750)
(1.296875,0.171875)
(1.148438,0.585938)
(1.574219,0.292969)
(1.287109,0.646484)

これは、以下のプログラムを実行することで描かれる。
num = input("How many points: ");

x = 0;
y = 0;
X(1) = x;
Y(1) = y;
for i = 2:num
    [x,y] = NextPoint(x,y);
    X(i) = x;
    Y(i) = y;
    printf("(%f,%f)\n",x,y)
endfor

hold on
plot(X,Y,"ob")   #  丸を描く
plot(X,Y)          # 線で結ぶ
xlim([0,2])
ylim([0,1])
title( sprintf("Sierpinski triangle, num=%d",num) )
hold off
描画点数Nを入力し、N個の点の座標を計算し、x座標、y座標をそれぞれ配列X,Yに溜め込んでから最後にプロットしているだけである。
プロットは、丸を描くのと、線で結ぶのを別々にしている。そのため、hold on/hold off により重ね描きを実現している。

次の点の座標を求める関数NextPointは以下のようになっている。

function [p,q] = NextPoint(x,y)
    switch randi(3)
    case 1
        p = 0.5*x;
        q = 0.5*y;
    case 2
        p = 0.5*x + 0.5;
        q = 0.5*y + 0.5;
    case 3
        p = 0.5*x + 1;
        q = 0.5*y;
    endswitch
endfunction

この次の点の座標の計算の仕方(意図)の説明は今回は省略する。 その代わり、この乱数フラクタルと呼ばれる方法は、この本で見つけたのだが、もっと色々な切り口で説明を試みる。
つまり、この遣り方はエレガントでないし、汎用性にも乏しいことは次回に説明する。

Oreilly_pytho_mathintro (213x300).jpgPythonからはじめる数学入門
Amit Saha 著
黒川 利明 訳
オライリー・ジャパン
2016/5/20 発行
A5, 304ページ
ISBN978-487311-768-3

第6章 幾何図形とフラクタルを描画する (p181-182)
問題6-2 シェルピンスキーの三角形
 より計算方法を拝借した。
Oreilly_pytho_mathintro (213x300).jpg

Pythonからはじめる数学入門
Amit Saha 著
黒川 利明 訳
オライリー・ジャパン
2016/5/20 発行
A5, 304ページ
ISBN978-487311-768-3

本書は、数学の入門書であり、かつPythonの数学に関連する主要なライブラリの入門書でもある。
というか、今はやりの人工知能、Deep Learning で Pythonがやたらに使われているのだが、Pythonの標準部分しか知らずに、急にPythonで書かれたDeep Learningのプログラムの勉強を始めると、Deep Learning自体を勉強する前に、基本的なことでつまづく可能性があるのだが、この本は、そのあたりのことを一通り教えてくれる。
でも、本書はDeep Learningのために書かれたのではなく、あくまでPythonで数学遊びができるようになるための入門書である。

本書を読む前に、一応Pythonの入門書を読み終えておいた方がよい。

数学については、せいぜい高校の数学程度の知識があれば十分だ。高校数学で挫折した人でも、本書で数学再入門が可能な本だ。

本書で教えてくれるPythonのライブラリは3つあり、その基本的な使い方を、数学的な素材を使いながら説明している。

最初にNumPyの説明がある。
NumPyは、数値計算などを行うための基本である。Python自体はリストを提供しているが、様々な数値計算、統計処理、技術計算などを行うとき、圧倒的に使うのが配列であり、行列である。
Pythonのリストで代用できないことはないが、猛烈に遅い。でも、NumPyを使って、配列演算、行列演算などを行うと、非常に高速に処理ができ、人工知能で要求される処理が可能になる。NumPyは、Deep Learning にはなくてはならないものである。

次に、matplotlibというグラフを描くためのライブラリの説明がある。
AIに限らないが、処理がうまくいっているのか破綻しているのかを数字の羅列で見るのは大変だ。でも、適切なグラフで示せば一目瞭然のことが多い。Deep Learning で学習の進行状態、過学習の有無など、本当に分かりやすくなる。

最後に、SymPyの説明がある。
これは、数値計算ではなく、式を記号のまま操作しようということである。方程式を、ちゃんと根号や記号が含まれたままで、処理しようということだ。
級数や微分なども、もちろん扱える。

これらのライブラリについて本気で知りたいときには、当然オリジナルを見に行こう。
http://www.numpy.org/
http://matplotlib.org/
http://www.scipy.org/

さまざまな情報が山のように提供されている。もちろん英語だけれど、プログラムの英語は簡単な中学英語レベルだから大丈夫だろう。

三角形というと、有名なのはやはりPascalの三角形だろう。
その形は以下のようになる。作り方は分かるだろうから省略する。

                           1
                         1   1
                       1   2   1
                     1   3   3   1
                   1   4   6   4   1
                 1   5  10  10   5   1
               1   6  15  20  15   6   1
             1   7  21  35  35  21   7   1
           1   8  28  56  70  56  28   8   1
         1   9  36  84 126 126  84  36   9   1
       1  10  45 120 210 252 210 120  45  10   1
     1  11  55 165 330 462 462 330 165  55  11   1
   1  12  66 220 495 792 924 792 495 220  66  12   1

下の図は、Pascalの三角形の数字が奇数だったら1、偶数だったらドット(.)に直したのが下図である。

                                                   1
                                                 1   1
                                               1   .   1
                                             1   1   1   1
                                           1   .   .   .   1
                                         1   1   .   .   1   1
                                       1   .   1   .   1   .   1
                                     1   1   1   1   1   1   1   1
                                   1   .   .   .   .   .   .   .   1
                                 1   1   .   .   .   .   .   .   1   1
                               1   .   1   .   .   .   .   .   1   .   1
                             1   1   1   1   .   .   .   .   1   1   1   1
                           1   .   .   .   1   .   .   .   1   .   .   .   1
                         1   1   .   .   1   1   .   .   1   1   .   .   1   1
                       1   .   1   .   1   .   1   .   1   .   1   .   1   .   1
                     1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1
                   1   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   1
                 1   1   .   .   .   .   .   .   .   .   .   .   .   .   .   .   1   1
               1   .   1   .   .   .   .   .   .   .   .   .   .   .   .   .   1   .   1
             1   1   1   1   .   .   .   .   .   .   .   .   .   .   .   .   1   1   1   1
           1   .   .   .   1   .   .   .   .   .   .   .   .   .   .   .   1   .   .   .   1
         1   1   .   .   1   1   .   .   .   .   .   .   .   .   .   .   1   1   .   .   1   1
       1   .   1   .   1   .   1   .   .   .   .   .   .   .   .   .   1   .   1   .   1   .   1
     1   1   1   1   1   1   1   1   .   .   .   .   .   .   .   .   1   1   1   1   1   1   1   1

これはどうみても、Sierpinskiの三角形である。 これらを描くプログラムは自分で作ろう。

奇数偶数ではなく、色々な規則を適用すると、様々な模様が現れるが、それをやるとキリが無いので止める。

num-akuma.jpg今回のPascalの三角形にはSierpinskiの三角形が隠れていることは、小学生向けの『数の悪魔』という本を参考にした。
小学校でもプログラミングが始まるようなので、こういう楽しい数遊びをする小学生が増えることを期待したい。

数の悪魔
――算数・数学が楽しくなる12夜
エンツェンスベルガー ベルナー絵 丘沢静也訳
晶文社
A5判 264頁 2000/4/1
定価:本体1600円+税
ISBN-13: 978-4794964540


横道にかなりそれてしまったので、次は最初のランダムに作るSierpinskiの三角形に話を戻す。

sierpinskirecex.png
Sierpinskiの三角形は、再帰的にどんどん三角形を細かくしているように見える。
つまり、右図において、与えられた三角形PQRに対して、P、Q、Rの対辺の中点をp、q、rとしたとき、

△PRQ ⇒ △Pqr, △pQr, △pqR

という風に、3つのサイズが半分になった三角形3つを描くのを、延々と再帰的に繰り返すだけなので、とても簡単なプログラムで実現できる。

今回は、もうちょっと工夫して、1つの画面にレベルを1つずつ増やしながら描くというのをやってみた。

sierpinskirec1.png
1;

function draw_triangle(P,Q,R,n)
    n = n - 1;
    if n == 0
        X = [P(1),Q(1),R(1),P(1)];
        Y = [P(2),Q(2),R(2),P(2)];
        plot(X,Y)
    else
        p = (Q+R)/2;
        q = (R+P)/2;
        r = (P+Q)/2;
        draw_triangle(P,q,r,n);
        draw_triangle(p,Q,r,n);
        draw_triangle(p,q,R,n);
    endif
endfunction

for num=1:6
    subplot(3,2,num)
    hold on
    draw_triangle([0,0],[2,0],[1,1],num);
    hold off
    title( sprintf("Sierpinski triangle, level=%d",num) );
endfor

プログラムの最初に
1;
という不思議な、明らかにゴミと思われるのが入っているが、これには訳がある。 これを取り去ると、エラーが出て動かなくなる。 理由は、Octaveでは、ファンクションとスクリプトのファイルが区別されてしまうからである。 ファイルの最初に関数宣言があると、関数のファイルとみなされ、そのファイルにスクリプトを書いてしまうとエラーになってしまう。
 これを避けるための方法が、最初に何の意味も無いスクリプトを書いて、このファイルはスクリプトであると思わせておいて、その後に関数を書き、関数の後ろに本来のスクリプトを書いたのである。 この程度の短いプログラムでも再帰があると関数をつかうことになる。これは、そういうときの裏技の1つである。


レベル6では、まだ綺麗というところまでいかないので、レベル8を以下に示す。

sierpinskirec8.png
(0,0)を出発点とし、ある規則、といってもランダム性を含む規則なのだが、点を次々と移動しながら、最初の100点を表示してみた。なんだか三角形がぼんやり見える程度である。
sierpinski100 (400x300).jpg一気に1000点まで表示すると、下図のようになり、規則性がなんとなく分かった感じになる。
sierpinski1000 (400x300).jpg最初の5000点まで描いてみた。
ここまでくると、どうやらフラクタル図形のシェルピンスキーの3角形を描いているように見える。
sierpinski5000 (400x300).jpg
さらに増やして、20000点まで増やしたが、点の大きさが災いして、細かいところまで表示できなくなってしまっている。
sierpinski20000 (400x300).jpgもっと精緻にしようということで、点を小さくし、画像も大きいままにしてみた。今回は1,000,000点である。
点数を増やしてもっと綺麗にするのはキリが無いので、このあたりで終わりにする。
sierpinski1000000.png

さて、フラクタル図形だから、普通は再帰で描くものと考えるだろうが、この図はある種の方法でランダムに点を打つことでフラクタル図形を作っている。

ということで、この図形について何回かに分けて紹介していこうと思う。

introPython3.jpg

入門Python3
Bill Lubanovic 著
斎藤 康毅 監訳
長尾 高弘 訳
オライリー・ジャパン
2015/12/1 発行
A5, 600ページ
ISBN978-4-87311-738-6



2000年頃にZope、やや遅れてPloneに使われている言語としてPythonが騒がれたのだが、その後長い間なりを潜めていたのだが、去年くらいから急にPythonの人気が盛り返した気がする。
急に脚光を浴びているのは、人工知能関連でよく使われている言語としてであろう。大学で最初に教える言語がPythonのところもちらほら見かけるようになった。

Pythonの本は非常にたくさん出ている。いや、毎日Python関係の新刊書が出ているのではないかと思われるほどある。Amazonでちょっと調べたが、何百冊も出てきて、うんざりするほどあった。
そういう状況なので、良さそうと思って読んだ本の中から、気が向いたものを紹介していこうと思う。

当方は、ちょっとAIのプログラムをいじろうと思ったら、Pythonが使われているのに気づいたものの、Pythonは2000年の頃に少しかじったきりで、もはや記憶の彼方にいってしまっているので、再度勉強しようということで、本書をちらっと読んで、試してみた。本書を選んだのは、非常に入手が簡単ということで選んだ。本屋まで行くこともなく、Amazonで届くのを待つことも無く、もっと短時間に割引購入できるからである。詳細は略す。


本書は、あくまでも入門書である。プログラミングが初めての人にはちょっと難しく感じるかもしれないが、他のプログラミング言語の経験があれば、この本1冊でPython3の入門は十分であろう。オライリーの他の本に慣れている人にはちょうどお手ごろなPython入門書と思われた。

Python3 ということは、Python2という旧版が存在するわけで、実はまだかなり使われているのだが、今から勉強するのなら、古い版をわざわざ勉強しなくても良いだろう。ただし、メンテナンスなどの場合は、バージョンの違いを意識しなくてはいけない。
Python関連の本を入手するとき、Python3対応かどうかは必ず調べた方が良い。また、また発行日、翻訳書の場合は原著の発行日を調べ、特別の理由がなければ2015年以降くらいの本に限定する方が無駄なトラブルを避けられる。

本文は12章、427ページまであるが、「第7章プロのようにデータを操る」(216ページ)まで読めばPython3の基本は抑えられる。このあたりまでを一気読みしよう。
Pythonで書かれた人工知能系の本を読むのが本来の目的なら、この先を読むよりも、人工知能でよく使われるPythonの拡張モジュールの勉強をする方が先であろう。そのあたりについては、また別に書こう。




偏微分方程式の数値解を求めるのに大きなサイズの逆行列を計算する必要があった。
これに限らず、色々な計算をやっていると、逆行列を求める必要にせまられる。それも、かなり大きなサイズで。

ということで、今回は、サイズn の正方行列の逆行列の計算量が、サイズnに対してどのように増えていくかを調べてみる。
要するに、逆行列の計算量のオーダーを求めようということだ。

まず、元のサイズnの正方行列を用意しなくてはならない。それも、逆行列が作れる行列、つまり正則行列を用意しなければならない。
ということで、
A = rand(n);
を考えた。要するに、n*nの正方行列に、0から1の範囲の一様乱数を散りばめただけの正方行列である。
しかし、不安もあるので、これで大丈夫か実験してみよう。

まず、乱数行列を作って、1つの行の半分の値を他の行に書き込むことで正則でなくしてから逆行列を作ってみた。
>> A = rand(4);
>> A(4,:) = A(1,:)/2
A =

   0.66039   0.64815   0.31595   0.95572
   0.45772   0.80134   0.98210   0.55091
   0.15502   0.47775   0.87121   0.86565
   0.33020   0.32407   0.15798   0.47786

>> B = A^-1
warning: inverse: matrix singular to machine precision, rcond = 0
B =

   Inf   Inf   Inf   Inf
   Inf   Inf   Inf   Inf
   Inf   Inf   Inf   Inf
   Inf   Inf   Inf   Inf

>>
これで、叱られ方が分かった。

次に、サイズ1000の乱数配列の逆行列を10000回作って大丈夫かどうか確かめた。
>> for i = range(10000)
A = rand(1000);
B = A^-1;
endfor
>>
何も小言は言われなかったので、いい加減にやって大丈夫のようだ。

次に、行列のサイズを100から100ずつ増やして計算時間を tic(), toc() を使って計測し、グラフ化してみた。
k = 1
for n = 100:100:5500
    A = rand(n);
    tic()
    A = A**(-1);
    sz(k) = n;
    tm(k) = toc()/r;
    k += 1;
endfor
plot(sz,tm)
invtime1.pngサイズの増大とともに急激に時間が掛かっているのは分かるのだが、指数関数的なのか、nの何乗かのオーダーなのかよく分からない。サイズが5400で止まっているのは、メモリ不足でOctaveに叱られたためである。

ということで、まず、指数関数的になっているのか調べるために、片対数のグラフを描いてみた。
semilogy(sz,tm)
invtime2.png指数関数的には増えていない。もっと緩やかな上昇をしていることが分かる。

最初のグラフから、たぶんn^3程度の割合で計算量が増えていそうなので、計算時間の3乗根(1/3乗)を縦軸にとったグラフを描いてみた。
plot(sz,tm.^(1/3))
invtime3.pngということで、逆行列の計算量は、ほぼO(n^3)と考えられる。
すると、2次元定常熱伝導の偏微分方程式を解く場合、格子点がn*n個の場合、係数行列のサイズがn^2となり、計算量はO(n^6)となって計算がすぐに事実上不可能になる。なお、メモリ量はO(n^4)となり、こちらも厳しい状態になる。

ところで、グラフを見て不審に思ったことはないだろうか。
あまりにも綺麗な曲線になっている。
それは、上で示したプログラムでは各サイズ1回だけ時間計測しているのだが、実際には20回計測して平均値を求めているので、滑らかになっている。1回だけだと、かなりギザギザになる。

このアーカイブについて

このページには、2016年10月に書かれたブログ記事が新しい順に公開されています。

前のアーカイブは2016年9月です。

次のアーカイブは2016年11月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。