2016 年ふりかえり

今年は 30 分でもいいから毎日コードを書くことを目標にしていたが、 ぜんぜん達成できなかった。

後半の方では時間を確保できる日が増えてきたので、 挽回できるかと思いきや、 肝心の MacBook Proカーネルパニックを頻繁に起こすようになり、 とてもコードを書ける状況じゃなくなった。 大誤算。

あと、家のネット環境が変わったのも影響が大きかった。 今まで使っていた au 光の回線を節約のために解約し、 マンションで提供されている回線を使うようにしたら、 これが遅いのなんの。 夜はネットを見るのがせいぜいで、大きめのファイルのダウンロードはまったく出来なかった。 いやはや、ここまで遅いとはね。

Feedly電子書籍で最低限のインプットはできているので、引き続き課題はアウトプットだな。 ブログはなんとか更新できているけど、プロダクトは今年もリリースできなかった。 新しい MacBook Pro を購入して、マシンスペックが上がったので、 来年こそはバリバリコードを書いていきたい。

それにしても、毎年目標を立ててはいるものの、達成できたためしが無いな。 とても自己採点なんてできない。

『アンサー』を買った

BUMP OF CHICKEN の新曲『アンサー』が iTunes Store で配信されていたので購入。

アンサー

アンサー

新曲が出ると数日は1曲リピートで聞き続けるんだけど、 今回も言うに及ばずリピートし続けている。

特に響いたのが

失くしたくないものを 見つけたんだって気づいたら こんなに嬉しくなって こんなに怖くなるなんて

という歌詞。 この2~3年は、子供が生まれたり、身内が立て続けに亡くなったりと、 生と死を身近に感じることが多かったから、 臆病になってしまった。 今の自分はまさにこの歌詞ような心情だ。

ちなみに、この曲はアニメ『3月のライオン』のオープニング曲だったらしい。 アニメは見てないけど、PV は公式の YouTube で見た。 そして、この曲がオープニング曲だった時のエンディング曲は『ファイター』。 両方同じアーティストというのはかなり珍しいんじゃないだろうか。

認証に xAuth を使う Web API を Python で呼び出すメモ

requests と標準ライブラリだけを使って書いてみた。 パラメータの指定、特に oauth_signature の作成がシンドイ。 Authorization の指定も。

# -*- coding: utf-8 -*-
from random import getrandbits
from time import time
import hmac
import hashlib 
import urllib
import urlparse
import requests

CONSUMER_KEY = "your consumer_key"
CONSUMER_SECRET = "your consumer_secret"
ACCESS_TOKEN_URL = "access token url"
WEB_API_URL = "web api url"
USERNAME = "your user name"
PASSWORD = "your password"

def escape(s):
    if not isinstance(s, bytes):
        s = s.encode("utf-8")
    return urllib.quote(s, safe="~")

# xAuth に必要なパラメーターを作成
payload = {
    "oauth_consumer_key": CONSUMER_KEY,
    "oauth_signature_method": "HMAC-SHA1",
    "oauth_timestamp": str(int(time())),
    "oauth_nonce": str(getrandbits(64)),
    "oauth_version": "1.0",
    "x_auth_mode": "client_auth",
    "x_auth_username": USERNAME,
    "x_auth_password": PASSWORD,
}
payload["oauth_signature"] = hmac.new(
  "%s&%s" % (CONSUMER_SECRET, ""),
  "&".join([
      "POST",
      escape(ACCESS_TOKEN_URL),
      escape("&".join(["%s=%s" % (escape(x), escape(payload[x]))
          for x in sorted(payload)]))
  ]),
  hashlib.sha1).digest().encode("base64").strip()

# xAuth のアクセストークンを取得
r = requests.post(ACCESS_TOKEN_URL, params=payload)
print r.text
token = urlparse.parse_qs(r.text)
oauth_token = token["oauth_token"][0]
oauth_token_secret = token["oauth_token_secret"][0]

# Web API を呼び出すためのパラメータを作成
payload = {
    "oauth_consumer_key": CONSUMER_KEY,
    "oauth_signature_method": "HMAC-SHA1",
    "oauth_timestamp": str(int(time())),
    "oauth_nonce": str(getrandbits(64)),
    "oauth_version": "1.0",
    "oauth_token": oauth_token,
}
payload["oauth_signature"] = hmac.new(
    "%s&%s" % (CONSUMER_SECRET, oauth_token_secret),
    "&".join([
      "GET",
      escape(WEB_API_URL),
      escape("&".join(["%s=%s" % (escape(x), escape(payload[x]))
          for x in sorted(payload)]))
    ]),
    hashlib.sha1).digest().encode("base64").strip()

# Web API 呼び出し
headers = {
    "Authorization": "OAuth %s" % escape(",".join(
        ['%s="%s"' % (escape(x), escape(payload[x])) for x in payload]))
}
r = requests.get(WEB_API_URL, params=payload, headers=headers)
print r.text

新型 MacBook Pro を買った

家で使っている MacBook Proカーネルパニックを頻繁するようになり、開発どころかネットを見ることですら支障をきたすようになった。一度、初期状態に戻してみたけど、改善しない。これは寿命が来たのだろうか。購入してから 4年以上は立つからなぁ。

Apple Care はとっくの昔に切れたし、ちょうど新型 MacBook Pro も発売されたので、思い切って買い替えることにした。

購入したのは新型 MacBook Pro 13インチ TouchBar モデル。SSD が 512GB のやつをカスタマイズして、メモリを 16GB に増やした。それで予算オーバーになったので CPU は据え置き。

www.apple.com

注文してから待つこと2週間。ようやく届いた。

開封の儀。

まず最初に、本体の薄さと軽さに驚いた。前の MacBook Pro は 13 インチとは言え、とても持って歩きたいとは思えない重量だったのに対し、新しい MacBook Pro は鞄に入れて毎日通勤しても問題無いレベル。実際にはやらないけど。

次に Retina ディスプレイの解像度の高さにビビった。じつは初 Retina。 良さはさんざん聞かされたけど、ここまで美麗だったなんて。 そんな Retina ディスプレイになって嬉しいのは、もう iOS シミュレーターのサイズを半分に設定しなくて済むことだったりするのだけども。

SSD かつ、メモリを 16GB に増量したおかげで、なにもかもが爆速に感じる。そういえば SSD も初体験。2016 年も終わりになってようやくとは、エンジニアの端くれとして情けない気もするが、だって金ないねん…。Safari がキビキビ動くのは当然として、前のマシンでは動作が非常にもっさりしていた SoureTree でさえも、まるで違うアプリのようにサクサク動く。これが金の弾丸か!動作が重いアプリの代表格である IDE はまだインストールできていないが、Visual Studio for Mac なんかは前のマシンの時とは比べ物にならないくらい機敏な動作になるのではと期待している。

あと、自分はファンクションキーをほとんど使わない人間だったので、1日触ったぐらいでは TouchBar に変わったことによる影響は感じられなかった。ただ、タッチタイピングしていると、ほとんど TouchBar を見ないような。視界の下で表示がなんか切り替わっているな、というのは認識しているけど、わざわざ視線を下に移動しないな。自分の場合は。

超単純な 3 層ニューラルネットワークを実装

C# でゼロから Deep Learnig を実装する挑戦の続き。 今回は超単純な 3 層ニューラルネットワークを実装してみた。 といっても、『ゼロから作る Deep Learnig』の 3 章の写経みたいなもの。

C# + Math.NET Numerics で試行錯誤しながら書いたコードは次の通り。

using MathNet.Numerics.LinearAlgebra;
using System;

namespace ThreeLayerNeuralNetworkSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var network = new ThreeLayerNeuralNetwork();
            var x = Vector<double>.Build.DenseOfArray(new double[] { 1.0, 0.5 });
            var y = network.Forward(x);
            Console.WriteLine(y);
            Console.ReadLine();
        }
    }

    /// <summary>
    /// 3 層ニューラルネットワークを表します。
    /// </summary>
    public class ThreeLayerNeuralNetwork
    {
        /// <summary>
        /// 第 1 層の重み
        /// </summary>
        readonly Matrix<double> W1;

        /// <summary>
        /// 第 1 層のバイアス
        /// </summary>
        readonly Vector<double> B1;

        /// <summary>
        /// 第 2 層の重み
        /// </summary>
        readonly Matrix<double> W2;

        /// <summary>
        /// 第 2 層のバイアス
        /// </summary>
        readonly Vector<double> B2;

        /// <summary>
        /// 第 3 層の重み
        /// </summary>
        readonly Matrix<double> W3;

        /// <summary>
        /// 第 3 層のバイアス
        /// </summary>
        readonly Vector<double> B3;

        /// <summary>
        /// <see cref="ThreeLayerNeuralNetwork"/>
        /// クラスの新しいインスタンスを初期化します。
        /// </summary>
        public ThreeLayerNeuralNetwork()
        {
            W1 = Matrix<double>.Build.DenseOfArray(new double[,]
            {
                { 0.1, 0.3, 0.5 },
                { 0.2, 0.4, 0.6 }
            });
            B1 = Vector<double>.Build.DenseOfArray(new double[] {
                0.1, 0.2, 0.3
            });
            W2 = Matrix<double>.Build.DenseOfArray(new double[,]
            {
                { 0.1, 0.4 },
                { 0.2, 0.5 },
                { 0.3, 0.6 }
            });
            B2 = Vector<double>.Build.DenseOfArray(new double[] {
                0.1, 0.2
            });
            W3 = Matrix<double>.Build.DenseOfArray(new double[,]
            {
                { 0.1, 0.3 },
                { 0.2, 0.4 }
            });
            B3 = Vector<double>.Build.DenseOfArray(new double[] {
                0.1, 0.2
            });
        }

        public Vector<double> Forward(Vector<double> x)
        {
            // ベクトルと行列の内積は * で計算できる
            // 活性化関数にはシグモイド関数を使う
            var a1 = x * W1 + B1;
            var z1 = Sigmoid(a1);

            var a2 = z1 * W2 + B2;
            var z2 = Sigmoid(a2);

            // 第 3 層では活性化関数ではなく恒等関数を使う
            // つまり、そのまま出力層に渡す
            var a3 = z2 * W3 + B3;
            var y = IdentityFunction(a3);

            return y;
        }

        /// <summary>
        /// シグモイド関数
        /// </summary>
        /// <param name="a">ベクトル</param>
        /// <returns>活性化後のベクトル</returns>
        private Vector<double> Sigmoid(Vector<double> a)
        {
            return a.Map(x => 1 / (1 + Math.Exp(-x)));
        }

        /// <summary>
        /// 恒等関数
        /// </summary>
        /// <param name="a">ベクトル</param>
        /// <returns><paramref name="a"/> をそのまま返します</returns>
        private Vector<double> IdentityFunction(Vector<double> a)
        {
            return a;
        }
    }
}

実行結果がこちら。

『ゼロから作る Deep Learnig』と同じ結果が出力できている。

Vector<T>Matrix<T>演算子オーバーロードしてくれているおかげで、 ベクトルと行列の内積を良い感じで記述できた。 Math.NET Numerics なかなかやるな。

MNIST データセットを読み込んでベクトルに変換

C# でゼロから Deep Learning を実装する挑戦の続き。 この挑戦では、『ゼロから作る Deep Learning』同様に、 手書き数字認識のニューラルネットワークを実装するので、 MNIST データセットを利用する。

MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges

MNIST データセットを読み込んで、 Math.NET Numerics の Vector に変換するコードを書いてみた。

using MathNet.Numerics.LinearAlgebra;
using System;
using System.IO;
using System.Linq;

namespace MnistSample
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                Console.WriteLine("MnistSample.exe <pixelsFilePath> <labelsFilePath>");
                Console.WriteLine("Press enter to quit");
                Console.ReadLine();
                return;
            }


            var cancel = false;
            Console.CancelKeyPress += (sender, e) =>
            {
                cancel = true;
            };

            // MNIST データセットをロードする
            var images = MnistImage.Load(pixelFilePath: args[0], labelFilePath: args[1]);
            Console.WriteLine($"Image count : {images.Length}");

            // 入力したインデックスにある画像の情報を表示する
            Console.WriteLine($"Press <Ctrl> + <C> to quit");
            while (cancel == false)
            {
                Console.WriteLine($"Input index (0 ~ {images.Length - 1})");

                var input = Console.ReadLine();
                int index;
                if (int.TryParse(input, out index) &&
                    (0 <= index) &&
                    (index <= images.Length))
                {
                    var image = images[index];
                    Console.WriteLine($"label : {image.Label}");
                    Console.WriteLine(image.ToVector());
                }
            }
        }
    }

    /// <summary>
    /// MNIST データセットの画像を表します。
    /// </summary>
    public class MnistImage
    {
        /// <summary>
        /// 画像の高さを取得します。
        /// </summary>
        public int Height { get; }

        /// <summary>
        /// 画像の幅を取得します。
        /// </summary>
        public int Width { get; }

        /// <summary>
        /// MNIST 画像のピクセルを取得します。
        /// </summary>
        public byte[][] Pixels { get; }

        /// <summary>
        /// 0 ~ 9 までのラベルを取得します。
        /// </summary>
        public byte Label { get; }

        /// <summary>
        /// <see cref="MnistImage"/> クラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <param name="height">画像の高さ</param>
        /// <param name="width">画像の幅</param>
        /// <param name="pixels">画像を構成するピクセル</param>
        /// <param name="label">ラベル</param>
        public MnistImage(int height, int width, byte[][] pixels, byte label)
        {
            Height = height;
            Width = width;
            Label = label;
            Pixels = new byte[height][];
            for (var i = 0; i < height; i++)
            {
                Pixels[i] = new byte[width];
            }
            for (var i = 0; i < height; i++)
            {
                for (var j = 0; j < width; j++)
                {
                    Pixels[i][j] = pixels[i][j];
                }
            }
        }

        // Vector<T> と Matrix<T> は byte をサポートしていない

        /// <summary>
        /// ベクトルに変換します。
        /// </summary>
        /// <returns>ベクトル</returns>
        public Vector<double> ToVector()
        {
            var flatten = Pixels.SelectMany(row => row)
                .Select(b => Convert.ToDouble(b));
            var vector = Vector<double>.Build.DenseOfEnumerable(flatten);
            return vector;
        }

        /// <summary>
        /// 行列に変換します。
        /// </summary>
        /// <returns>行列</returns>
        public Matrix<double> ToMatrix()
        {
            return Matrix<double>.Build.Dense(Height, Width, (row, col) => Pixels[row][col]);
        }

        /// <summary>
        /// MNIST データセットとラベルをロードします。
        /// </summary>
        /// <param name="pixelFilePath">MNIST データセットのパス</param>
        /// <param name="labelFilePath">ラベルのパス</param>
        /// <returns><see cref="MnistImage"/> の配列</returns>
        public static MnistImage[] Load(string pixelFilePath, string labelFilePath)
        {
            using (var imageStream = File.OpenRead(pixelFilePath))
            using (var labelStream = File.OpenRead(labelFilePath))
            using (var imageReader = new BinaryReader(imageStream))
            using (var labelReader = new BinaryReader(labelStream))
            {
                int magic1 = imageReader.ReadInt32();
                magic1 = ReverseBytes(magic1);

                // 画像の枚数を取得
                int imageCount = imageReader.ReadInt32();
                imageCount = ReverseBytes(imageCount);

                // 画像の高さを取得
                int imageHeight = imageReader.ReadInt32();
                imageHeight = ReverseBytes(imageHeight);

                // 画像の幅を取得
                int imageWidth = imageReader.ReadInt32();
                imageWidth = ReverseBytes(imageWidth);

                int magic2 = labelReader.ReadInt32();
                magic2 = ReverseBytes(magic2);

                // ラベルの個数を取得
                int labelCount = labelReader.ReadInt32();
                labelCount = ReverseBytes(labelCount);

                // 読み込んだ1枚分の画像データを格納するバッファを作成
                var pixels = new byte[imageHeight][];
                for (var i = 0; i < pixels.Length; i++)
                {
                    pixels[i] = new byte[imageWidth];
                }

                // 読み込んだすべての MNIST 画像を格納する配列を作成
                var result = new MnistImage[imageCount];

                for (int di = 0; di < imageCount; di++)
                {
                    for (int i = 0; i < imageHeight; i++) // get 28x28 pixel values
                    {
                        for (int j = 0; j < imageWidth; j++)
                        {
                            byte b = imageReader.ReadByte();
                            pixels[i][j] = b;
                        }
                    }

                    // ラベルを取得
                    byte label = labelReader.ReadByte();

                    var image = new MnistImage(imageHeight, imageWidth, pixels, label);
                    result[di] = image;
                }

                return result;
            }
        }

        /// <summary>
        /// 整数のビットを逆順にします。
        /// </summary>
        /// <param name="value">整数</param>
        /// <returns>ビットを逆順にした整数</returns>
        public static int ReverseBytes(int value)
        {
            byte[] intAsBytes = BitConverter.GetBytes(value);
            Array.Reverse(intAsBytes);
            return BitConverter.ToInt32(intAsBytes, 0);
        }
    }
}

読み込んだデータセットのうち、指定した位置の画像情報を出力できるようにしてある。 試しに 3 を入力してみた結果がこちら。

活性化関数とソフトマックス関数を実装

『ゼロから作る Deep Learning』を読んで C# で実装したみたくなった続き。 今度はニューラルネットワークで使う関数に挑戦してみた。

実装するのは活性化関数であるシグモイド関数、ReLU 関数。 そして出力層で使うソフトマックス関数。 あとおまけでステップ関数も。

ひとまず畳み込まないニューラルネットワークを作りたいので、 行列を扱う活性化関数として実装。 ソフトマックス関数は出力層で使うので、こいつだけはベクトルを扱う。

using MathNet.Numerics.LinearAlgebra;
using System;
using System.Linq;

namespace NeuralNetworkSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var m = Matrix<double>.Build.Random(3, 3);
            Console.WriteLine(m);

            Console.WriteLine("step function");
            Console.WriteLine(StepFunction(m));

            Console.WriteLine("ReLU");
            Console.WriteLine(ReLU(m));

            Console.WriteLine("sigmoid");
            Console.WriteLine(Sigmoid(m));

            var v = m.Column(0);
            Console.WriteLine("softmax");
            Console.WriteLine(Softmax(v));

            Console.ReadLine();
        }

        public static Matrix<double> StepFunction(Matrix<double> a)
        {
            // 行列の要素ごとの演算に Map を使う
            return a.Map(x => x > 0 ? 1.0 : 0.0);
        }

        public static Matrix<double> Sigmoid(Matrix<double> a)
        {
            return a.Map(x => 1 / (1 + Math.Exp(-x)));
        }

        public static Matrix<double> ReLU(Matrix<double> a)
        {
            return a.Map(x => Math.Max(x, 0));
        }

        public static Vector<double> Softmax(Vector<double> a)
        {
            var c = a.Max();

            // ベクトルの要素ごとの演算にも Map を使う
            var exp_a = a.Map(x => Math.Exp(x - c));
            var sum_exp_a = exp_a.Sum();
            var y = exp_a.Map(x => x / sum_exp_a);
            return y;
        }
    }
}

実行結果はこちら。

正しく動いている、ように見える。 今回も Math.NET Numerics を使ってみたが、NumPy と比べると前回以上に冗長に感じた。 NumPy はよくできたライブラリだ。