
Outline
CNTK 2.2 Python API 入門 – パッケージ構成、基本演算 & 順伝播型 NN 分類器
0. はじめに
CNTK ( Microsoft Cognitive Toolkit ) 2.2 の Python API 入門です。
CNTK は、公開当初はスマートな Caffe ツールというフレーバーのインターフェイスが基本でしたが、現在では普通に Python API が公開されています。つまり、他の深層学習フレームワークと同様に CNTK でも Python プログラムを作成することで深層学習モデルが構築できます。(書きやすいという評判のフレームワークの多くと同様に) CNTK のニューラルネットワークは Python の関数オブジェクトで表現されます。私見では、他のフレームワークよりも straightforward にコーディングできますし、ネットワーク定義もトレーニング・ループも非常に簡潔に書くことができます。
しかしながら CNTK の進化の経緯もあり、Python プログラミングに関する情報が少ないように思われますので、CNTK 2.2 Python API の入門記事を作成してみました。CNTK 2.2 Tutorials への橋渡しになることを心掛けて作成しました。
内容は :
- CNTK 2.2 Python API コンセプト
- CNTK Python パッケージ構成
- CNTK 基本演算
- 順伝播型ニューラルネットワーク分類器のコード解読
- What’s Next ?
目標は CNTK Python API を利用して、下図のような分離可能なランダムデータを分類する簡単なニューラルネットワークを作成して、トレーニングそして評価することです。
入門という位置づけですので、最初に CNTK Python API のコンセプトとパッケージ構成について説明します。
そして基本演算の課題をこなしてからネットワーク構築を行ないます。この基本演算とは cntk.ops パッケージにより提供される、CNTK 計算グラフのノードとなるプリミティブです。単純なテンソル数値演算、論理演算そして活性化関数も含みます。更には入力テンソルを供給するための変数 – プレースホルダーも含んでいます。
- 特に難しいことはしていませんので、Python, NumPy 及び機械学習の基礎知識があれば十分に理解できるかと思います。
- NumPy をご存知なくても、CNTK の数値演算が element-wise であること、つまりテンソル (多次元配列) の要素に沿って計算が可能であることを理解して頂ければ十分です。
動作環境の構築が必要な場合には、Cognitive Toolkit 2.2 を Azure Linux GPU 仮想マシンにインストール を参考にしてください。Azure ポータルと Ubuntu Linux にある程度慣れていれば、30 分程度で以下のような環境が構築できるかと思います :
- Azure NC 仮想マシン with NVIDIA Tesla® K80 GPU
- Ubuntu 16.04 LTS
- NVIDIA CUDA 8.0 & cuDNN 6.0
- Anaconda 3 4.1.1
- CNTK 2.2 (for GPU)
分類器の具体的な題材としては、上記事中でも CNTK の動作確認に利用した、チュートリアルの FeedForwardNet.py を使用します。(このサンプルは Getting started でも使用されています。)
本記事を最後まで読めば (基本事項が盛り込んである、) 以下の CNTK Python プログラムが十分に理解できて実行可能になります。
import numpy as np
from cntk.device import cpu, try_set_default_device
from cntk import Trainer
from cntk.layers import Dense, Sequential, For
from cntk.learners import sgd, learning_rate_schedule, UnitType
from cntk.ops import input_variable, sigmoid
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.logging import ProgressPrinter
# make sure we get always the same "randomness"
np.random.seed(0)
def generate_random_data(sample_size, feature_dim, num_classes):
# Create synthetic data using NumPy.
Y = np.random.randint(size=(sample_size, 1), low=0, high=num_classes)
# Make sure that the data is separable
X = (np.random.randn(sample_size, feature_dim) + 3) * (Y + 1)
X = X.astype(np.float32)
# converting class 0 into the vector "1 0 0",
# class 1 into vector "0 1 0", ...
class_ind = [Y == class_number for class_number in range(num_classes)]
Y = np.asarray(np.hstack(class_ind), dtype=np.float32)
return X, Y
# Creates and trains a feedforward classification model
def ffnet():
input_dim = 2
num_output_classes = 2
num_hidden_layers = 2
hidden_layers_dim = 50
# Input variables denoting the features and label data
feature = input_variable((input_dim), np.float32)
label = input_variable((num_output_classes), np.float32)
netout = Sequential([For(range(num_hidden_layers), lambda i: Dense(hidden_layers_dim, activation=sigmoid)),
Dense(num_output_classes)])(feature)
ce = cross_entropy_with_softmax(netout, label)
pe = classification_error(netout, label)
lr_per_minibatch = learning_rate_schedule(0.5, UnitType.minibatch)
# Instantiate the trainer object to drive the model training
learner = sgd(netout.parameters, lr=lr_per_minibatch)
progress_printer = ProgressPrinter(128)
trainer = Trainer(netout, (ce, pe), learner, progress_printer)
# Get minibatches of training data and perform model training
minibatch_size = 25
for i in range(1024):
features, labels = generate_random_data(
minibatch_size, input_dim, num_output_classes)
# Specify the mapping of input variables in the model to actual
# minibatch data to be trained with
trainer.train_minibatch({feature: features, label: labels})
trainer.summarize_training_progress()
test_features, test_labels = generate_random_data(
minibatch_size, input_dim, num_output_classes)
avg_error = trainer.test_minibatch(
{feature: test_features, label: test_labels})
return avg_error
if __name__ == '__main__':
# Specify the target device to be used for computing, if you do not want to
# use the best available one, e.g.
# try_set_default_device(cpu())
error = ffnet()
print(" error rate on an unseen minibatch %f" % error)
1. CNTK 2.2 Python API コンセプト
最初に CNTK 2.2 Python API のコンセプトを知るために CNTK Library API の説明を見ておきます (以下の引用は翻訳です) :
CNTK Python API は、モデル定義と計算、学習アルゴリズム、データ・リーディングと分散訓練のための抽象モジュールから構成されます。
柔軟性と簡潔性 : これらの抽象モジュールはあらゆるニューラルネットワークの定義と訓練において、柔軟性と簡潔さの両者を直交性 (独立性) が高く提供するための基礎となります。
効率的なデータ・インターフェイス : 単純ですがしかし軽量のデータ・インターフェイスはネイティブな numpy 配列の形式のデータを計算エンジンに効率的に供給することを可能にします。
組み込みデータ・リーダー: 画像、テキスト・フォーマットと音声 HTK (Hidden markov model ToolKit, 隠れマルコフモデル・ツールキット) データフォーマットのための、CNTK 組み込みである最適化されスケーラブルなデータ・リーダーもまた Python
API から利用可能です。これはユーザがデータを読むためのコードを作成することなしに既存のデータで直接トレーニングすることを容易にします。
高度にスケーラブルな学習 : API は高度にスケーラブルなトレーニング性能を公開しています (1-Bit SGD のような並列アルゴリズム)。分散トレーニング・サンプル はトレーニング並列 API を示します。
簡潔なネットワーク定義 : API は、CNTK V1 に類似した再起性 (= recurrence) を含む簡潔で高度なニューラルネットワーク定義を可能にする高位な 層ライブラリ を含みます。ツールキットは、再起ステップの静的展開を要求する代わりにネットワークにおける循環としてのシンボリック・フォームのリカレント・モデルの表現をサポートします。より一般的には、これはリカレント・ニューラルネットワークの簡潔で効率的な表現と実行という結果になります。CNTK Python API の総てのコア計算、学習、そしてデータ・リーディング API 抽象モジュールは Python と C++ の両者から非常に簡単に拡張可能で、新しい演算子、学習主体 (= learner) そしてデータ・リーダーを簡単に実装することを可能にします、これはライブラリの組み込み機能で自由に構成できます。
API は新しい プロトコルバッファ ベースのモデル・シリアライゼーションを導入します、これはセーブされたモデルの後方そして前方互換性をサポートします。
データ・インターフェイスを NumPy ベースにしたのは賢明な判断と言えましょう。あくまで私的見解ですが、期待したい点として以下をあげておきます :
- 深層学習フレームワークを利用する場合、データを読み込んで供給するまでのコーディングに時間が割かれるケースが多々ありますが、かなりの改善が期待できそうです。
- ネットワーク定義の簡潔さ。この性質は今回扱うサンプルでも見て取れますが、特にリカレント・ニューラルネットワークの定義に期待しましょう。
- スケーラブルな分散トレーニング。分散トレーニングについては、今のところ他のフレームワークでは柔軟性の欠如・実装の難しさなどの点から決め手に欠けていると考えていますので、CNTK には大いに期待したいところです。
次に、プログラマ視点からもう少し具体的に見ていきましょう。
2. CNTK Python パッケージ構成
Python ライブラリとして site-packages/cntk にインストールされた CNTK ライブラリのパッケージ構成を確認します :
$ ls -F
axis.py debugging/ __init__.py libs/ ops/ tensor.py
cntk_py.py default_options.py internal/ logging/ __pycache__/ tests/
conftest.py device.py io/ losses/ pytest.ini train/
contrib/ eval/ layers/ metrics/ random/ variables.py
core.py initializer.py learners/ misc/ sample_installer.py
ライブラリ構成は標準的な深層学習フレームワークの構成と言って良いでしょう。
なお、learners は学習アルゴリズムを実装したオプティマイザーと考えて良いです。
次に、サブモジュールとサブパッケージについて Python API リファレンス – cntk パッケージ を参照して、提供される機能を確認してみましょう。これらは冒頭に示したサンプル・プログラムでもインポートされて利用されています。
2-1 cntk サブパッケージ
パッケージ名
提供される機能
cntk.contrib
CNTK のための特別なユティリティ、例えば、他の深層学習ツールキットへの橋渡しをするユティリティ。
cntk.debugging
cntk.debugging.debug, cntk.debugging.profiler
cntk.eval
モデル評価のためのユティリティ。
cntk.io
CNTK I/O ユティリティ。
cntk.layers
CNTK 層ライブラリ。
cntk.learners
learner はトレーニング・プロセスの間パラメータ・セットの調整をします。パラメータの異なるセットのために異なる learner を使用できます。現在、CNTK は次の学習アルゴリズムをサポートしています : AdaDelta, AdaGrad, FSAdaGrad, Adam, MomentumSGD, Nesterov, RMSProp, SGD
cntk.logging
ロギングのためのユティリティ。
cntk.losses
損失関数。
cntk.metrics
評価メトリクス。
cntk.misc
CNTK のための様々なユティリティ、例えば、CNTK 計算グラフのノードをフィルタリングして変換します。
cntk.ops
CNTK コア演算子。これらの演算子の呼び出しは CNTK 計算グラフのノードを作成します。
cntk.random
指定された分布に関する乱数を生成する関数。
cntk.train
トレーニングのためのユティリティ。
2-2 cntk サブモジュール
モジュール名
提供される機能
cntk.axis
データ・バインディングにおける CNTK 変数のための Axis(軸)。
cntk.core
コア数値構築、NDArrayView, View クラス。
cntk.default_options
CNTK 関数のデフォルト・オプション。
cntk.device
CNTK 計算がその上で実行されるデバイスを指定するユティリティ。
cntk.initializer
パラメータの初期化子。
cntk.sample_installer
CNTK サンプルデータを取得するためのユティリティ。
cntk.tensor
テンソル演算。
cntk.variables
CNTK 変数、パラメータ、定数、そしてレコード。
3. CNTK 基本演算
ここから具体的なオペレーションを行ないます。CNTK に慣れるために、cntk.ops パッケージの演算子を各種試してみます。
最初に重要なノードであるプレースホルダーにふれた後で、簡単な数値演算・論理演算を実行していきます。
※ 冒頭でも言及しましたが、動作環境の構築の構築が必要な場合には Cognitive Toolkit 2.2 を Azure Linux GPU 仮想マシンにインストール を参考にしてください。
Python インタープリタを起動したら、準備として最初に numpy と cntk を import して CNTK のバージョンの確認をしておきましょう :
>>> import numpy as np
>>> import cntk as C
>>> C.__version__
'2.2'
これらの演算子は総て CNTK 計算グラフのノードと考えて良いです。共通の eval() メソッドは、そのノードのために forward パス (順伝播計算) を実行した時に呼び出し可能で forward パスの結果を返します。
NumPy の基礎知識があれば理解しやすいですが、そうでない場合でも以下を抑えておけば問題ありません :
- 演算の多くは element-wise です、つまり配列の要素ごと、あるいは要素に渡る計算が可能です。
- 説明文に出てくる「ブロードキャスト」は、大雑把に言えば、複数の入力配列の ndim/shape (次元/形状) がミスマッチな場合でも ndim/shape を自動調節した上で計算してくれる便利な機能です。
3-1 プレースホルダー
cntk.ops.input_variable() はネットワークの入力を作成する、モデル作成に必須のノードで、特徴やラベルのようなデータをモデルに供給するために使用されます。CNTK のドキュメントではプレースホルダーという呼称は使用していませんが、いわゆるプレースホルダーと同義と考えて良いです。
>>> x = C.input_variable(2)
>>> y = C.input_variable(2)
>>> x0 = np.asarray([[2., 1.]], dtype=np.float32)
>>> y0 = np.asarray([[4., 6.]], dtype=np.float32)
>>> C.squared_error(x, y).eval({x:x0, y:y0})
Selected GPU[0] Tesla K80 as the process wide default device.
array([ 29.], dtype=float32)
この例では、最初に2つの入力変数 x, y を shape(1,2) で設定しています。
そしてこれら2つの変数を入力として squared_error (二乗誤差) ノードを作成しています。
squared_error の eval メソッドの内側ではこれら2つの変数のためにデータの入力マッピングが設定できます。
この例では2つの NumPy 配列を渡していて、二乗誤差は (2-4)**2 + (1-6)**2 = 29 となります。
3-2 定数定義
cntk.ops.constant() は NumPy 配列から定数テンソルを作成します。
>>> constant_data = C.constant([[1., 2.], [3., 4.], [5., 6.]])
Selected GPU[0] Tesla K80 as the process wide default device.
>>> constant_data.value
array([[ 1., 2.],
[ 3., 4.],
[ 5., 6.]], dtype=float32)
>>> constant_data.asarray()
array([[ 1., 2.],
[ 3., 4.],
[ 5., 6.]], dtype=float32)
パラメータ、定数、値、etc. のようなデータ・コンテナの多くは asarray() メソッドを実装していて、これらは NumPy インターフェイスを返します。
>>> c = C.constant(3, shape=(2,3))
Selected GPU[0] Tesla K80 as the process wide default device.
>>> c.asarray()
array([[ 3., 3., 3.],
[ 3., 3., 3.]], dtype=float32)
>>> np.ones_like(c.asarray())
array([[ 1., 1., 1.],
[ 1., 1., 1.]], dtype=float32)
3-3 簡単な数値演算
次に、cntk.ops の数値演算ノードを列挙していきます。最初は数値演算の中でも簡単なものをあげていきます。
※ 以後、出力文字列 “Selected GPU[0] Tesla K80 as the process wide default device.” は省略します。
abs – 要素ごとに絶対値を計算します :
>>> C.abs([-1, 1, -2, 3]).eval()
array([ 1., 1., 2., 3.], dtype=float32)
ceil – いわゆる天井関数で、入力値以上の最小の整数を要素ごとに計算します。
>>> C.ceil([0.2, 1.3, 4., 5.5, 0.0]).eval()
array([ 1., 2., 4., 6., 0.], dtype=float32)
>>> C.ceil([[0.6, 3.3], [1.9, 5.6]]).eval()
array([[ 1., 4.],
[ 2., 6.]], dtype=float32)
cos – 要素ごとに三角関数の cosine (コサイン) を計算します。
np.round(C.cos(np.arccos([[1,0.5],[-0.25,-0.75]])).eval(),5)
array([[ 1. , 0.5 ],
[-0.25, -0.75]], dtype=float32)
exp – 要素ごとに指数を計算します。
>>> C.exp([0., 1.]).eval()
array([ 1. , 2.71828175], dtype=float32)
floor – いわゆる床関数で、入力値以下の最大の整数を要素ごとに計算します。
>>> C.floor([0.2, 1.3, 4., 5.5, 0.0]).eval()
array([ 0., 1., 4., 5., 0.], dtype=float32)
>>> C.floor([[0.6, 3.3], [1.9, 5.6]]).eval()
array([[ 0., 3.],
[ 1., 5.]], dtype=float32)
>>> C.floor([-5.5, -4.2, -3., -0.7, 0]).eval()
array([-6., -5., -3., -1., 0.], dtype=float32)
>>> C.floor([[-0.6, -4.3], [1.9, -3.2]]).eval()
array([[-1., -5.],
[ 1., -4.]], dtype=float32)
log – 要素ごとに対数を計算します。
>>> C.log([1., 2.]).eval()
array([ 0. , 0.69314718], dtype=float32)
minus – 減算です。ブロードキャストもサポートしています。
※ ブロードキャストは、複数の入力配列の ndim/shape (次元/形状) がミスマッチな場合でも、ndim/shape を自動調節した上で計算してくれる機能です。
>>> C.minus([1, 2, 3], [4, 5, 6]).eval()
array([-3., -3., -3.], dtype=float32)
>>> C.minus([[1,2],[3,4]], 1).eval()
array([[ 0., 1.],
[ 2., 3.]], dtype=float32)
negate – 要素ごとの negation (否定、符号反転) を計算します。
>>> C.negate([-1, 1, -2, 3]).eval()
array([ 1., -1., 2., -3.], dtype=float32)
plus – 加算。ブロードキャストもサポートしています。
>>> C.plus([1, 2, 3], [4, 5, 6]).eval()
array([ 5., 7., 9.], dtype=float32)
>>> C.plus([-5, -4, -3, -2, -1], [10]).eval()
array([ 5., 6., 7., 8., 9.], dtype=float32)
>>> C.plus([-5, -4, -3, -2, -1], [10], [3, 2, 3, 2, 3], [-13], [+42], 'multi_arg_example').eval()
array([ 37., 37., 39., 39., 41.], dtype=float32)
>>> C.plus([-5, -4, -3, -2, -1], [10], [3, 2, 3, 2, 3]).eval()
array([ 8., 8., 10., 10., 12.], dtype=float32)
reciprocal – 要素ごとの逆数を計算します。
>>> C.reciprocal([-1/3, 1/5, -2, 3]).eval()
array([-3. , 5. , -0.5 , 0.33333334], dtype=float32)
sin – 要素ごとに三角関数の sin (サイン) を計算します。
>>> np.round(C.sin(np.arcsin([[1,0.5],[-0.25,-0.75]])).eval(),5)
Selected CPU as the process wide default device.
array([[ 1. , 0.5 ],
[-0.25, -0.75]], dtype=float32)
3-4 論理演算
次に基本的な論理演算です。これも簡単でしょう。
equal – 2つのテンソルの要素ごとの比較です。値が同じ場合には 1 を返し、そうでない場合には 0 を返します。
>>> C.equal([41., 42., 43.], [42., 42., 42.]).eval()
array([ 0., 1., 0.], dtype=float32)
>>> C.equal([-1,0,1], [1]).eval()
array([ 0., 0., 1.], dtype=float32)
greater(left, right) – 2つのテンソルの要素ごとの比較です。left > right の場合には 1 を返し、そうでない場合には 0 を返します。
>>> C.greater([41., 42., 43.], [42., 42., 42.]).eval()
array([ 0., 0., 1.], dtype=float32)
>>> C.greater([-1,0,1], [0]).eval()
array([ 0., 0., 1.], dtype=float32)
greater_equal(left, right) – 2つのテンソルの要素ごとの比較です。left >= right の場合には 1 を返し、そうでない場合には 0 を返します。
>>> C.greater_equal([41., 42., 43.], [42., 42., 42.]).eval()
array([ 0., 1., 1.], dtype=float32)
>>> C.greater_equal([-1,0,1], [0]).eval()
array([ 0., 1., 1.], dtype=float32)
less(left, right) – 2つのテンソルの要素ごとの比較です。left < right の場合には 1 を返し、そうでない場合には 0 を返します。
>>> C.less([41., 42., 43.], [42., 42., 42.]).eval()
array([ 1., 0., 0.], dtype=float32)
>>> C.less([-1,0,1], [0]).eval()
array([ 1., 0., 0.], dtype=float32)
less_equal(left, right) – 2つのテンソルの要素ごとの比較です。left <= right の場合には 1 を返し、そうでない場合には 0 を返します。
>>> C.less_equal([41., 42., 43.], [42., 42., 42.]).eval()
array([ 1., 1., 0.], dtype=float32)
>>> C.less_equal([-1,0,1], [0]).eval()
array([ 1., 1., 0.], dtype=float32)
not_equal – 2つのテンソルの要素ごとの比較です。left != right の場合には 1 を返し、そうでない場合には 0 を返します。
>>> C.not_equal([41., 42., 43.], [42., 42., 42.]).eval()
array([ 1., 0., 1.], dtype=float32)
>>> C.not_equal([-1,0,1], [0]).eval()
array([ 1., 0., 1.], dtype=float32)
3-5 少し高度な数値演算
これらは参考までにあげておきます。機械学習では argmax, argmin は良く使います。
acos – 要素ごとに arccos を計算します。arccos (逆余弦) は cos の逆関数です。
>>> np.round(C.acos([[1,0.5],[-0.25,-0.75]]).eval(),5)
array([[ 0. , 1.04719996],
[ 1.82348001, 2.41885996]], dtype=float32)
argmax(x, axis=None) – 指定された軸に沿って要素の argmax を計算します (インデックスを返す点に注意してください)。
>>> data = [[10, 20],[30, 40],[50, 60]]
>>> C.argmax(data, 0).eval()
array([[ 2., 2.]], dtype=float32)
>>> C.argmax(data, 1).eval()
array([[ 1.],
[ 1.],
[ 1.]], dtype=float32)
argmin(x, axis=None) – 指定された軸に沿って要素の argmin を計算します。
>>> data = [[10, 30],[40, 20],[60, 50]]
>>> C.argmin(data, 0).eval()
array([[ 0., 1.]], dtype=float32)
>>> C.argmin(data, 1).eval()
array([[ 0.],
[ 1.],
[ 1.]], dtype=float32)
asin – 要素ごとに arcsin を計算します。arcsin (逆正弦) は sin の逆関数です。
>>> np.round(C.asin([[1,0.5],[-0.25,-0.75]]).eval(),5)
array([[ 1.57079995, 0.52359998],
[-0.25268 , -0.84806001]], dtype=float32)
clip(x, min_value, max_value) – min_value と max_value の間に収まるようにクリップします。
>>> C.clip([1., 2.1, 3.0, 4.1], 2., 4.).eval()
array([ 2. , 2.0999999, 3. , 4. ], dtype=float32)
>>> C.clip([-10., -5., 0., 5., 10.], [-5., -4., 0., 3., 5.], [5., 4., 1., 4., 9.]).eval()
array([-5., -4., 0., 4., 9.], dtype=float32)
cosh – 要素ごとに cosh (hyperbolic cosine, 双曲線余弦関数) を計算します。
>>> np.round(C.cosh([[1,0.5],[-0.25,-0.75]]).eval(),5)
array([[ 1.54307997, 1.12763 ],
[ 1.03140998, 1.29468 ]], dtype=float32)
element_divide – 2つの入力に対して要素ごとの除算を行ないます。ブロードキャストもサポートしています。
>>> C.element_divide([1., 1., 1., 1.], [0.5, 0.25, 0.125, 0.]).eval()
array([ 2., 4., 8., 0.], dtype=float32)
>>> C.element_divide([5., 10., 15., 30.], [2.]).eval()
array([ 2.5, 5. , 7.5, 15. ], dtype=float32)
element_max – 2つまたはそれ以上のテンソルの要素ごとの max を計算します。ブロードキャストもサポートしています。
>>> C.element_max([1., 1., 1., 1.], [0.5, 0.25, 0.125, 0.]).eval()
array([ 1., 1., 1., 1.], dtype=float32)
>>> C.element_max([1., 2., 3., 4., 5., 6., 7. ,8.], 4).eval()
array([ 4., 4., 4., 4., 5., 6., 7., 8.], dtype=float32)
element_min – 2つまたはそれ以上のテンソルの要素ごとの min を計算します。ブロードキャストもサポートしています。
>>> C.element_min([1., 1., 1., 1.], [0.5, 0.25, 0.125, 0.]).eval()
array([ 0.5 , 0.25 , 0.125, 0. ], dtype=float32)
>>> C.element_min([1., 2., 3., 4., 5., 6., 7. ,8.], 4).eval()
array([ 1., 2., 3., 4., 4., 4., 4., 4.], dtype=float32)
element_times – 2つまたはそれ以上のテンソルの要素ごとの積を計算します。ブロードキャストもサポートしています。
>>> C.element_times([1., 1., 1., 1.], [0.5, 0.25, 0.125, 0.]).eval()
array([ 0.5 , 0.25 , 0.125, 0. ], dtype=float32)
>>> C.element_times([5., 10., 15., 30.], [2.]).eval()
array([ 10., 20., 30., 60.], dtype=float32)
>>> C.element_times([5., 10., 15., 30.], [2.], [1., 2., 1., 2.]).eval()
array([ 10., 40., 30., 120.], dtype=float32)
log_add_exp – 2つまたはそれ以上の入力テンソルの指数の和の対数を計算します。
>>> a = np.arange(3,dtype=np.float32)
>>> np.exp(C.log_add_exp(np.log(1+a), np.log(1+a*a)).eval())
array([ 2., 4., 8.], dtype=float32)
>>> np.exp(C.log_add_exp(np.log(1+a), [0.]).eval())
array([ 2., 3., 4.], dtype=float32)
pow(base, exponent) – べき乗。base (底) を exponent (指数) でべき乗します。ブロードキャストもサポートしています。
>>> C.pow([1, 2, -2], [3, -2, 3]).eval()
array([ 1. , 0.25, -8. ], dtype=float32)
>>> C.pow([[0.5, 2],[4, 1]], -2).eval()
array([[ 4. , 0.25 ],
[ 0.0625, 1. ]], dtype=float32)
reduce_log_sum_exp(x, axis=None) – 指定された軸に渡り入力テンソルの要素の指数の総和の log を計算します。
>>> data = np.array([[[5,1], [20,2]],[[30,1], [40,2]],[[55,1], [60,2]]], dtype=np.float32)
>>> C.reduce_log_sum_exp(data, axis=0).eval().round(4)
array([[[ 55. , 2.09859991],
[ 60. , 3.09859991]]], dtype=float32)
>>> np.log(np.sum(np.exp(data), axis=0)).round(4)
array([[ 55. , 2.09859991],
[ 60. , 3.09859991]], dtype=float32)
3-6. その他
次章でも使用しますが、活性化関数 (sigmoid, tanh, relu, leaky_relu 等)、更には dropout も cntk.ops パッケージに含まれています。
4. 順伝播型ニューラルネットワーク分類器のコード解読
さて、基礎知識は得られましたので本記事の目的である、冒頭に掲載したサンプル・プログラムを解読して実行してみましょう。
題材とするサンプル・プログラムは FeedForwardNet.py です。
“python -m cntk.sample_installer” でサンプルをインストールした場合には、ディレクトリ “~/CNTK-Samples-2-2/Tutorials/NumpyInterop/” 下に配備されます。
サンプルの内容を簡単に説明しておくと、NumPy で生成したランダムな合成データ (2 次元特徴データとラベル) を訓練/テスト・データとして、隠れ層2つを持つ順伝播型ニューラルネットワーク分類器をトレーニングして評価します。
4-1 コード解説
デバイス設定
サンプルでは該当コードがコメントアウトされていますが、最初にデバイス設定についてふれておきます。
CNTK でネットワークを訓練し実行するにあたり、どのデバイス上でそれが実行されるべきかを決定することができます。
言うまでもなく GPU へアクセスできれば訓練時間は大幅に短縮されます。
明示的にデバイスを GPU に設定するには、cntk.device.try_set_default_device() 関数を使用して次のようにしてターゲットデバイスを設定します :
>>> from cntk.device import try_set_default_device, gpu
>>> try_set_default_device(gpu(0))
True
因みに、CPU-only 環境で GPU デバイスの設定にいくと、”Specified GPU device id (0) is invalid.” というエラーになります :
>>> from cntk.device import try_set_default_device, gpu
>>> try_set_default_device(gpu(0))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/masao/anaconda3/lib/python3.6/site-packages/cntk/internal/swig_helper.py", line 69, in wrapper
result = f(*args, **kwds)
File "/home/masao/anaconda3/lib/python3.6/site-packages/cntk/device.py", line 96, in gpu
return cntk_py.DeviceDescriptor.gpu_device(device_id)
ValueError: Specified GPU device id (0) is invalid.
[CALL STACK]
...
インポート
さて、それではサンプルのコードを具体的に見ていきましょう。まずは cntk のサブモジュールやサブパッケージを numpy とともに import します。もちろん、”2. CNTK Python パッケージ構成” でまとめたものが利用されています。
import numpy as np
from cntk.device import cpu, try_set_default_device
from cntk import Trainer
from cntk.layers import Dense, Sequential, For
from cntk.learners import sgd, learning_rate_schedule, UnitType
from cntk.ops import input_variable, sigmoid
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.logging import ProgressPrinter
データ生成
generate_random_data 関数は numpy を利用して分離可能なランダムデータ – 2 次元特徴ベクトル X とラベル Y を生成します。合成データとは言え、ラベルから特徴を生成するという不自然なことをしているために少し分かりにくくなっていますので、詳しく説明しておきます。
def generate_random_data(sample_size, feature_dim, num_classes):
# NumPy を使用して合成データを作成する。
Y = np.random.randint(size=(sample_size, 1), low=0, high=num_classes)
# データが分離可能であることを確かなものとする
X = (np.random.randn(sample_size, feature_dim) + 3) * (Y + 1)
X = X.astype(np.float32)
# クラス 0 をベクトル "1 0 0 " に変換し、
# クラス 1 はベクトル "0 1 0" へ, ...
class_ind = [Y == class_number for class_number in range(num_classes)]
Y = np.asarray(np.hstack(class_ind), dtype=np.float32)
return X, Y
具体的な生成手順としては :
(1) まず二値の shape (25,1) のラベル Y を生成します。一回目に生成されたラベル Y を出力すると以下のようになっています :
[[0] [1] [1] [0] [1] [1] [1] [1] [1] [1] [1] [0] [0] [1] [0] [0] [0] [0] [0] [1] [0] [1] [1] [0] [0]]
(2) 相関関係を持たせるために Y を算入して shape (25, 2) の特徴ベクトル X を生成します。一回目に生成される特徴 X は以下のようなものです :
[[ 4.12531424 2.64100456] [ 8.44121647 3.32100892] [ 6.85674667 5.75307369] [ 4.41437721 2.87594938] [ 10.01631451 6.45977306]
[ 7.20978737 9.25431919] [ 9.18912125 6.46086836] [ 5.87017918 4.06203938] [ 7.18248558 4.4344492 ] [ 5.11153412 5.30962753]
[ 4.2363987 5.11469364] [ 2.4590838 1.67677259] [ 2.88720107 3.90734601] [ 7.63053989 6.45819569] [ 1.97382116 3.47752547]
[ 4.29269838 2.26854181] [ 1.39459777 3.9894762 ] [ 3.11081457 2.61906862] [ 3.11495924 3.3453126 ] [ 2.53008246 9.31670189]
[ 5.29977131 2.52886462] [ 8.52543068 3.65896916] [ 8.13157845 4.60012531] [ 3.14407921 3.39854217] [ 3.0268693 4.05583715]]
(3) 訓練 (そして評価) 用にラベル Y を one-hot ベクトルに変換します。
※ one-hot ベクトルは一つの要素だけが 1 で他の要素が 0 のベクトルです。扱いやすさのために機械学習では良くこの表現が使用されます。
※ このケースは 2 次元であるために分かりにくいですが、(1) のデータ表現と一つずつ比較すれば何をしているか理解しやすいと思います。
[[ 1. 0.] [ 0. 1.] [ 0. 1.] [ 1. 0.] [ 0. 1.] [ 0. 1.] [ 0. 1.] [ 0. 1.] [ 0. 1.] [ 0. 1.]
[ 0. 1.] [ 1. 0.] [ 1. 0.] [ 0. 1.] [ 1. 0.] [ 1. 0.] [ 1. 0.] [ 1. 0.] [ 1. 0.] [ 0. 1.]
[ 1. 0.] [ 0. 1.] [ 0. 1.] [ 1. 0.] [ 1. 0.]]
(4) そして呼び出し側に X, Y を返します。
【補足】
直感的に分かりやすくするために、matplotlib を使用して生成されたデータを可視化してみました。
ラベル 0 を青色・ラベル 1 を赤色として、1 回目から 4 回めまでのミニバッチ生成データを可視化すると、以下のようになります。
もちろん分離可能で、従って分類可能です :
プレースホルダー
プレースホルダーについては “3-1 プレースホルダー” で説明しましたが、入力変数 feature と label を作成しています。
トレーニング・ループでこれらにミニバッチとしてデータが供給されることによって、モデル・パラメータの最適化が進んでいくことになります。2つの入力変数 feature, label はいずれも shape(1,2) で設定されています。label に供給されるデータは one-hot ベクトル表現に変換されていることに注意しましょう。
input_dim = 2
num_output_classes = 2
...
# 特徴とラベルデータを示す入力変数
feature = input_variable((input_dim), np.float32)
label = input_variable((num_output_classes), np.float32)
ネットワーク定義
CNTK のネットワーク定義は、冒頭でも言及しましたが Python 関数オブジェクトとして実装されます。
Python ライクな記法も使用されていて驚くほど簡潔です :
num_hidden_layers = 2
hidden_layers_dim = 50
...
netout = Sequential([For(range(num_hidden_layers), lambda i: Dense(hidden_layers_dim, activation=sigmoid)),
Dense(num_output_classes)])(feature)
このままでも十分に直感的に分かりやすいのですが、念のために仕様を確認しておくと :
-
Sequential は層のリストを引数に取る、層ファクトリです。 -
For (cntk.layers.For) は python の “for” に類似した層ファクトリ関数です。lambda 記法が利用できます。 -
Dense は完全結合層のインスタンスを作成するための層ファクトリ関数です。
つまり、全体として次のような構造を持つ順伝播 (feedforward) ニューラルネットワークが定義されていることになります :
- 入力 (層) は特徴ベクトルのミニバッチである feature を受け取る
- 隠れ層 1 : 50 ユニットを持つ完全結合層 (活性化関数 : sigmoid)
- 隠れ層 2 : 50 ユニットを持つ完全結合層 (活性化関数 : sigmoid)
- 出力層 (分類層) : 分類クラス数 2 のための完全結合層
損失関数・評価メトリクス
損失関数としては定石どおり cross_entropy_with_softmax() が使用されています。
この関数は入力に対して Softmax を適用した上で交差エントロピーを計算しています。
大雑把に言えば、確率分布に直した上でネットワーク出力とラベルの差異をエントロピーという乱雑さを表わす尺度を利用して計算しています。
ce = cross_entropy_with_softmax(netout, label)
pe = classification_error(netout, label)
classification_error() の方は分類エラーを計算する評価メトリクスです。
評価メトリクスは必須なものではなく、また微分可能でなくても良いです。Trainer オブジェクトに渡すことによってモデルのクオリティを分かりやすい表現で追跡できます。
learner
learner は最初はオプティマイザーと考えれば分かりやすいのですが、学習の主体くらいの意味でしょう。
cntk.learners.sgd はもちろん学習アルゴリズムとして SGD が実装されていて
引数としてはネットワーク・パラメータのリストと learning_rate_schedule オブジェクトを取ります。
cntk.learners パッケージには他にも cntk.learners.adadelta, adagrad, adam, momentum_sgd, rmsprop などが実装されています。なお、Adamax は adam のオプションで指定可能です。
lr_per_minibatch = learning_rate_schedule(0.5, UnitType.minibatch)
# Instantiate the trainer object to drive the model training
learner = sgd(netout.parameters, lr=lr_per_minibatch)
ProgressPrinter
トレーニング/評価の進捗を示すユティリティで、(損失やメトリクスのような) 様々な統計情報を出力することを可能にします。
引数の数字はミニバッチ数をベースとした表示頻度です。
progress_printer = ProgressPrinter(128)
Trainer
与えられたモデル・パラメータを、指定された損失関数を使用してトレーニングするためのクラスです。
モデル・パラメータの更新のためには、先に cntk.learners.sgd オブジェクトとして作成された learner が使用されます。
オプションでメトリクス関数を指定すれば訓練されたモデルのクオリティを追跡できます。
trainer = Trainer(netout, (ce, pe), learner, progress_printer)
トレーニング・ループ
Trainer オブジェクトを使用したトレーニング・ループは非常に簡潔です。
各種フレームワークの中でもこのトレーニング・ループは最も簡潔なものの一つと言えるでしょう。
ミニバッチを生成するたびに Trainer オブジェクトの train_minibatch メソッドに渡してやれば、モデル・パラメータが最適化されていきます。
for i in range(1024):
features, labels = generate_random_data(
minibatch_size, input_dim, num_output_classes)
# Specify the mapping of input variables in the model to actual
# minibatch data to be trained with
trainer.train_minibatch({feature: features, label: labels})
評価
評価のためには、Trainer オブジェクトの test_minibatch メソッドを使用してください。
trainer.summarize_training_progress()
test_features, test_labels = generate_random_data(
minibatch_size, input_dim, num_output_classes)
avg_error = trainer.test_minibatch(
{feature: test_features, label: test_labels})
4-2 まとめと実行
以上で、冒頭のサンプル・スクリプトの解読は完了です。
CNTK Python API では、モデル作成からトレーニングへの流れが非常に分かりやすいです。
復習も兼ねて、深層学習モデルのトレーニングにフォーカスして主要なコード・ピースだけをまとめて流れを追ってみると、以下のようになります :
# 特徴とラベルのためのプレースホルダーの作成
feature = input_variable((input_dim), np.float32)
label = input_variable((num_output_classes), np.float32)
# 順伝播ニューラルネットワークの定義
netout = Sequential([For(range(num_hidden_layers), lambda i: Dense(hidden_layers_dim, activation=sigmoid)),
Dense(num_output_classes)])(feature)
# 損失関数の指定
ce = cross_entropy_with_softmax(netout, label)
# 学習アルゴリズムの指定
learner = sgd(netout.parameters, lr=lr_per_minibatch)
# Trainer オブジェクトの作成
trainer = Trainer(netout, (ce, pe), learner, progress_printer)
# 訓練ループ
for i in range(1024):
# ミニバッチの作成
features, labels = generate_random_data(minibatch_size, input_dim, num_output_classes)
# ミニバッチ・データを供給してモデル・パラメータを調整
trainer.train_minibatch({feature: features, label: labels})
最後に、GPU 環境でサンプル・スクリプトを実行しておきましょう :
$ python FeedForwardNet.py
Selected GPU[0] Tesla K80 as the process wide default device.
-------------------------------------------------------------------
Build info:
Built time: Sep 15 2017 07:30:54
Last modified date: Fri Sep 15 04:28:48 2017
Build type: release
Build target: GPU
With 1bit-SGD: no
With ASGD: yes
Math lib: mkl
CUDA version: 9.0.0
CUDNN version: 6.0.21
Build Branch: HEAD
Build SHA1: 23878e5d1f73180d6564b6f907b14fe5f53513bb
MPI distribution: Open MPI
MPI version: 1.10.7
-------------------------------------------------------------------
Learning rate per minibatch: 0.5
Minibatch[ 1- 128]: loss = 0.614628 * 3200, metric = 29.25% * 3200;
Minibatch[ 129- 256]: loss = 0.332382 * 3200, metric = 13.31% * 3200;
Minibatch[ 257- 384]: loss = 0.298804 * 3200, metric = 11.59% * 3200;
Minibatch[ 385- 512]: loss = 0.271270 * 3200, metric = 10.44% * 3200;
Minibatch[ 513- 640]: loss = 0.247776 * 3200, metric = 9.03% * 3200;
Minibatch[ 641- 768]: loss = 0.231604 * 3200, metric = 9.06% * 3200;
Minibatch[ 769- 896]: loss = 0.229529 * 3200, metric = 8.62% * 3200;
Minibatch[ 897-1024]: loss = 0.214106 * 3200, metric = 8.25% * 3200;
Finished Epoch[1]: loss = 0.305012 * 25600, metric = 12.45% * 25600 4.119s (6215.1 samples/s);
error rate on an unseen minibatch 0.040000
5. What’s Next ?
- CNTK 2.2 Python API に興味が持てたのであれば、次は CNTK 2.2 Tutorials に取り組まれるのが確実でしょう。
- ロジスティック回帰、MNIST、CIFAR-10 といったお馴染みのテーマについて詳しく解説されています。
- シリアル番号 100 番台 (CNTK-101, 102, 103, 104, 105 & 106) が初級チュートリアルに該当しています。
- また、Layers Library Overview (層ライブラリ概要) も興味深いです。
以上
- JuliaでPyPlot.jl (matplotlib) を使ったヒストグラムのアニメーションを効率的に作成する
- 栄養素多次元ベクトル間の類似度を計算する
- Kerasのbackendが、思い通りならない時のメモ
- scikit-learnでSVMのパラメータを調節してみた話
- matplotlibでよく使う手続き
- Djangoのモジュールをインタープリタで読み込む
- PythonでRandom Forestを使う
- 文字と濁点・半濁点が分かれていて,それらを結合したい時
- NLTKを使って情報利得を計算
- PythonとTkinterを使ってプログラミング
- pythonのunix-time <-> datetime の変換
- Pythonの-mオプションを使えばフィルターの管理が楽
- doctest.testfile()を使ってドキュメント兼テストコードを作成
- カレントディレクトリをwebサーバで公開
- ニコニコ生放送のコメントを取得する
- tkinterを使ったアプリケーションをcx_freezeで実行ファイルにする
- ネスト解消のためのポリモーフィズムについて
- Google+で見かけたコメントに,「Pythonでリストの何番目の要素が最大/最小か求める簡単な方法は?」というのがあった.真っ先に思いついたのが1行目のコードなのだけれど,2行目の方がより良いかな?
- 日本語を含むUnicodeのJSON文字列を得る.
- Pythonでテストする際には何を使っているか?
- JSON変換するクラス
- FlaskでBlueprintを使ってControllerを別に分けてみる
- Pyramidで遊ぶ
- Pythonでconstant
- プログラミングの師匠募集
- ドミニオン圧縮プレイをモンテカルロ法で分析
- Pythonでテストを書いてプロファイル取ったりカバレッジ調べたり
- Pythonにおける例外のメッセージ
- 使ってないポートを取得する Python版
- 文字から Unicode を算出する
- どうやって情報収集してますか?
- Google App Engineのwebapp.RequestHandlerはputとdeleteを受けるときパラメータを取得できない
- 空のディレクトリに.gitkeepを配置するコマンドラインツール
- php でも ruby でも python でも perl でも動く Hello World
- amazon linux上で、python2.7用にMySQL-pythonをコンパイル・インストール
- # 文字列の文字コードを返す関数
- ImportError: cannot import nameを解消する
- JSON の整形
- pythonインタプリタ起動時に良く使うmoduleをimportする
- IPython起動時にスクリプトを自動的に読み込む
- UNIXでPython,Ruby,Perl,Node.jsのバージョン管理環境構築
- unicodeとdecodeとencodeについて実験した
- エロ動画フォルダからサムネイルをopencvで楽に作る
- appcfg.pyでデプロイしたファイルをダウンロードする
- OS X LionでPython開発環境を作成
- Django+MongoDB開発環境整備 (書き途中)
- List 操作メモ書き
- 引数のデフォルト値はimmutableなものにする
- すっと頭に再帰が浮かぶようになりたい
- Google App Engineのpythonのバージョンが噛み合わない問題について
- Pythonで数値計算
- 辞書のキーにタプルを使って、複数キー項目
- Pythonでflatten
- Xcode用自動ビルドバージョンインクリメントスクリプト
- Pythonでtest.pyを作るな!
- ファイル監視にwatchdogがかなり便利な件
- Flask on dotCloud でハマったところ
- easy_installでインストールしたパッケージを削除する方法
- pythonbrewでの複数バージョン&ライブラリ管理メモ
- Python de BDD (Lettuceで)
- CentOS 5.4 に Python 2.7.3 を install
- pip のコマンド/オプションのタブ補完を有効にする
- boto で S3 アップローダー
- 「オープンソースで学ぶ社会ネットワーク分析」におけるpythonコマンド
- 一度だけ評価するプロパティ
- SQLAlchemy ORM でクエリ生成の高速化
- Django の django-admin.py でパスを通しても command not found の時
- Django でデコレータを使って View への preprocess を実装してみる
- Pythonで関数合成
- Sublime Text で pythonbrew
- Paver を使ったスクリプティング – コマンド定義編
- Paver 応用編 – プロジェクトに組み込む
- Paver を使ったスクリプティング – ファイル操作編
- Paver を使ったスクリプティング – 外部コマンド編
- VPC with a Single Public Subnet Only を boto で自作
- 指定時間内に関数が終了しなかったら何かするデコレータ
- はじめてのDjangoで躓いた箇所まとめ
- BitbucketのOAuth:access token取得まで
- python でモック – mox の使い方
- Djangoのコマンド補完設定
- Python の Singleton
- すごく簡単なアルゴリズムがphpで書けなくてつらい
- Unix Domain Socket サーバーを作る
- プログラミングの学習に役立つウェブサイト
- DMMのAPIを叩くクラス
- letを作ってLet’s 1行プログラミング
- Setup modern Python environment with Homebrew
- Pygments.rb を使う
- Mailmanのwithlistコマンドによる一括設定変更
- ハイフン区切り形式の MAC アドレスを取得する -1 liner 編-
- Zsh上で使用頻度の高いコマンド一覧を表示する
- Python で複素数を扱う
- PyQt4導入
- 重み付け乱択
- Pythonのキーワード引数も含めてmemoizeしたい
- Ubuntuで個人環境にpythonパッケージをインストール
- pythonでUTF8のテキスト処理
- NLTKを使って文書分類用データをサクッと作る
- 最速最強Webサーバーアーキテクチャ
- 「Sublime Text 2で全角スペースをハイライト表示するプラグインを作る」に設定でオンオフ出来るようにしてみた。
- SublimeLinterにD言語(他の任意の言語)を追加してハイライト出来るようにする
- pythonの文法チェック
- defaultdictを使って,ネストされた辞書を作る
- SQLAlchemy BaseModel
- Python で Amazon CloudFront の Signed URL を発行する
- PyOpenCL Version 2012.2の話 第1弾
- TortoiseHgのファイルビューをもう少し改造してみる
- Docutils と Ruby で快適ドキュメント生活
- numpyの練習1
- スクリプト言語で実行可能なファイルを作る
- パラメトリックとノンパラメトリックの狭間
- bing search apiの使い方
- Flaskのカスタマイズについて
- PyOpenCLでアプリケーション(PIL & PyOpenGL)
- pytestを実戦投入してみた
- いとも簡単にMac OS XにSciPyをインストールする
- 実務でRandomForestを使ったときに聞かれたこと
- gaffer + fabric で簡単デプロイ環境
- Theano の 基本メモ
- 実行スクリプトからの相対パスでファイルにアクセスする.
- CoffeeScriptとJavaScriptやPythonやRubyの文法の比較
- これ知らないプログラマって損してんなって思う汎用的なツール
- 【まとめ】これ知らないプログラマって損してんなって思う汎用的なツール 100超
- Python での shift_jis と shift_jis_2004 について
- リストの中のインスタンスのもつ値で検索
- 日時 ⇔ 文字列
- ファイルの更新を検知して、自動的にCoffeeやSassをコンパイルしたり、ブラウザをリロードしたりするスクリプト
- curlを捨ててhttpieを使おう
- 都道府県のリストをサクッと出力するだけのCLIツール
- Python でホスト名を取得する
- MinGWでVimをビルド。(+lua,+python)
- 複数の辞書のマージ方法いろいろ
- LinuxでPythonをビルドするときの –enable-shared オプションについて
- pythonで二分探索
- Qiita API の Python ラッパーを作った
- Haskellソース編集後、保存と同時にghciにロードするSublime Text 2プラグイン
- botの一部の機能として自動フォローやランダムツイート
- kobitonote.py – Kobitoで編集したアイテムをEvernoteに同期保存
- Command Line で使用しているツールまとめ vol.5
- Flaskを1ヶ月間使ってみた感想
- Pythonのfeedparserをつかってみる。
- Pythonで文字列 <-> 日付(date, datetime) の変換
- Qiitaの投稿をGitHubにバックアップ
- Pythonプログラムのプロセス名を設定する
- Xcodeプロジェクト内のクラスの依存関係を図示する
- Python スクリプト実行中にシェルを起動する
- SQLAlchemyでINNER JOINする方法
- pymongoで正規表現
- Windows + Python 3.3 で watchdog をインストールする
- プログレスバー表示させたいなぁ
- Pythonでconfig.iniを使う
- Twistedを利用してお手軽にDNSサーバーを構築する
- 今流行りのエディタSublimeText2を使って、AOJで競技プログラミングを楽しもう
- PythonでN-Gram
- Parse.com REST APIを使ったAPN(remote notification)を試した
- Apache + mod_wsgi で複数の WSGIPythonPath を設定する
- クラスにバインディングしてYAMLの読み書き
- SublimeText2とSublimeLinter – Python3の構文チェック –
- Pinaxの概要について簡単なまとめ #djangoja
- アクセサを一つのメソッドにまとめる
- PythonのPILで書き出したGIF画像をつなげて動画GIFを作る
- Pythonで画像をWavelet変換するサンプル
- TheanoをMac OS Xにhomebrewでinstallする方法
- BitbucketのPrivate repositoryをタダでCIする
- Python3.3 + mod_wsgi3.4 をさくらVPS(CentOS)にインストールした時に少しハマったので振り返りメモ
- [Python2]日付文字列→UnixTime→日付文字列
- Install Theano on Ubuntu 12.04
- Vim (with python) で json を整形
- tweepy2導入
- PyramidのテンプレートをMakoに切り替える
- propertyを使うときはobjectを継承したクラス (new-style class) を使う
- redis-pyの使い方 辞書とか
- コマンドラインからtwitter投稿
- 標準入力の受け取りのその1
- Python – MP3ファイルにタグづけ
- ファイルを保存した瞬間ユニットテストを実行
- Python – Django css・jsをコンプレス
- requestsが便利
- リスト内に特定の要素があるかでif分岐
- Pythonの辞書 初心者向けガイド
- fabricのお役立ちコンテキストマネージャ
- FlaskでLast-Modifiedを設定する最も簡単な方法
- Flaskのrequest.argsでパラメータの処理について
- ブラウザでPython:Brythonのすすめ
- MeCab解析済みの結果を読み取るcorpus readerを書いた
- PythonでCIDR表記を変換
- multiprocessingで並列処理
- ログ監視スクリプト
- Ubuntu12.04にPython3.3をインストール
- 今更 virtualenv を使ってみる
- lxmlでブロークンなXMLをパースする
- 永続性が必要な時は hash() を使うな!
- Fabricタスクの途中で実行ユーザーを切り替えたいときはsettingsコンテキストマネージャ
- pythonからfluentdを使う
- Vimのpython3で日本語表示するの面倒くさすぎワロタwww
- MochaでブラウザテストをするためのWebサーバ
- 行数あたりの価値が高いコード
- Python で日付の計算
- コマンドラインで XLSX を CSV に変換する
- ファイルの中身を簡単な暗号化(Python)
- Pythonで文字列からさくっと文字の出現頻度を数えるには?
- GAE/Pyのdev_appserver.pyで外部(localhost以外)から接続可能にするには
- RubyとPythonじゃデフォルト引数の値が評価されるタイミングが違うんだぜ
- Python 3の print() で UnicodeEncodeError を回避するデコレータ
- Google Calendar から日報を生成する
- MeCab 使ってよみがなを取ってくる
- PyCharm での type hinting を使いこなす
- Django – Apache mod_wsgi virtualhost デプロイ
- ブラウザで保存したファイルをダウンロード元サイト別にフォルダ分け
- BitBucket に Sphinx ドキュメントを push したら自動的にウェブサーバーに反映するようにしてみた
- Sublime Text 2でPythonの開発環境を整える
- SciPyとmatplotlibのインストール(Python)
- Twistedを利用してメールサーバーを立てる
- SciPy+matplotlibで3D散布図を作成(Python)
- CentOS 6.x x86_64 Python-2.7.4 rpmパッケージ作成
- pythonの例外でstack traceを表示する
- lambdaでif-elifを楽に書く
- Python, SciPy, matplotlibのインストール(Windows)
- Python 3の正規表現で \d を使うな!
- CentOS6.4でPython2.7.3でApacheでmod_wsgiでDjango
- f2pyを使ってfortranでpythonのモジュールを書く
- ネットワークインタフェースに振られた IPv4 アドレスをコードで取得する (Linux)
- sphinx+mathjaxでマクロをusepackageみたいに読み込む
- iPythonでオートフォーマットされた出力の改行場所を変える
- Python3.3.1 in BottleでGoogle Custom Search APIを使って検索するだけのアプリケーションを作る
- Pythonでリフレクション
- pythonでhtml中の相対リンクを絶対リンクに書き換える(lxml)
- Pythonスクリプトからjarファイル中のクラスを直接importする
- class sklearn.naive_bayes.MultinomialNB(alpha=1.0, fit_prior=True, class_prior=None)のメモ
- pythonの軽量framework, Bottle
- GAE上でのTwilio
- リプ爆撃
- Twilogから過去ツイートを取ってきてbotを作り、昔の自分をTLに再現させるでござるの巻(その1)
- Macを使うにあたっての落とし穴回避(Linuxユーザ向け?)
- PyQCheckというPythonでQuickCheckが行えるライブラリをPyPIに登録した。
- multiprocessing vs threading
- MacにPythonをインストールするときのメモ
- Webアプリケーションのパフォーマンス測定ツールFunkload…
- python データーを圧縮してsqliteへ書き込む
- Zabbix APIをたたく (1.8, 2.0)
- emacs-jediのinstall
- Twilogから過去ツイートを取ってきてbotを作り、昔の自分をTLに再現させるでござるの巻(その2)
- テストデータはsetUpで宣言するべき幾つかの理由
- pythonをはじめるためのリンク
- a()とa.__call__()は等価ではない
- Python で簡易 SMTP サーバを立てる
- ログを色つけて見やすくする君
- 【LDAP環境構築:7】 Pythonでユーザー追加・検索・変更・削除まで
- classの__str__を、lambdaを使ってもっと簡単に書こう!
- MacにDjangoをインストール
- ChaSenのインストール
- CPythonからJavaのクラスを呼び出す(Py4J)
- テキストファイルのエンコーディングを自動判定して処理する
- pythonのsqlite3 に日本語(マルチバイト文字列)を格納する
- pythonのsqlite3でインメモリDBをセーブ/ロードする
- Python+FlaskなwebアプリをJenkinsでウイーン
- pyramidチュートリアル メモ (bankaccount)
- ローカルのipアドレスを取得
- macbook air Mid2013 にインストールしたアプリや開発環境のメモ(php,ruby,node.js,python など)
- PySideのスロットを定義する際の注意
- はてなブックマークのお気に入りユーザーで、既にアクティヴではないユーザーを調べる
- フォーマット文字列内での波括弧のエスケープ
- Pythonの数値計算ライブラリ NumPy入門
- PySideのスロットを定義する際の注意(2)
- vim7.3(+python2.4)をソースからインストール(Gundo.vimに対応)
- PythonからMySQLを使う
- コンソールへの出力を上書きしてゆく方法
- Pythonzとvirtualenvでクリーンなpython環境
- WEBブラウザからコマンドを実行する君作った
- ATND APIをPythonで叩くときのtips
- pytestを使ってカバレージとか取りながら分割実行してテスト実行時間を短縮する
- Pythonのクラス定義のファイルの場所を知る。
- encodeするのがめんどくさい
- はじめてのpython① pythonbrewで環境構築&HelloWorld!!
- Google Cloud Messaging for Chrome を使ってサーバからChrome拡張にメッセージを送る
- Pythonでコマンドライン引数にサブコマンドを指定する
- WEBブラウザから検索やコマンド実行をする君2
- 多重ループを一気に抜ける
- Pythonでgitのコミットログをパースしてみよう!
- Pythonで再帰的にファイル・ディレクトリを探して出力する
- 『Macで日本語ファイルをgitにコミットするのやめて><』とりあずMacとLinuxで互換性のない日本語ファイルを探すスクリプト書いた
- Flaskで大きいアプリケーションを作るときのTips
- 1分でVim+Pythonのテスト環境をつくる
- 深イイ意味など全くない並列処理 in Python
- 電波状況が悪い時にiPadにファイルを転送する
- matplotlibでstacked histogram (積み上げヒストグラム) を書く
- pipでtweepyをインストールしてAPI1.1に対応させて使う
- python2.7.3のcsvモジュールを使う時の文字エンコード
- Pythonで”coding: utf-8″ 書くの面倒くさいのでShellscriptでなんとかする
- Pythonで関数合成と適用的な
- pythonでフォルダ内のファイル名をrenameして整理したメモ
- 【作業メモ】Amazon Linuxにmatplotlibとnumpy入れるまで
- Appiumを使ってスマートフォンアプリのテストを自動化する – Python編
- matplotlibで欲しいグラフの書き方がわからない時はgalleryを見ると便利
- web2pyのadminのpassword
- PIL(Python Imaging Library)で、点を描画するサンプル。
- Mac OS Marvericks にHomebrewを使ってpyenv,pythonの環境設定を行うメモ
- PIL(Python Imaging Library)で、画像をセピア調に変換する
- PySide – モードレスダイアログを表示中にバックグラウンドで何らかの処理を行う
- Python用ケムインフォマティクスツールRDKitのインストール
- バックアップにBakthatいいんじゃね?
- Pythonのreduceを使うとちょうはかどる話
- Pythonにおける継承の基本的なお話(初心者向け)
- Pythonの古いクラスと新しいクラスの話
- unittest.mock
- MySQL Workbench の mwb ファイルから sql ファイルに自動変換
- ipythonの便利な使い方
- Propertyデコレータを使う?
- MySQLの全DBに対してmysqldumpするスクリプト
- pyramidチュートリアル メモ (single_file_tasks)
- Sublime Text 2で任意のテキストを常にハイライト表示するプラグインを作る
- 時間計測
- [python] lassieでWebページのメタデータをサクっととってくる
- オブジェクトの属性を取得
- classをクロージャーっぽく動的に宣言する
- MySQLdbの優雅な使い方
- PythonのiteratorとRubyのEnumeratorを比較してみた
- ImportError: No module named と言われた時の対応方法
- LXCをブラウザで操作できるLXC Web Panelが素晴らしかった件
- 非エンジニア向けのMercurial入門
- blogの更新を監視して結果をtwitterに投げたり、interviewsの回答を催促したり
- Pythonのモジュール例外の表示を分かりやすくする
- CentOS 6.4 で python から MySQL につなぐ
- EmacsのPython開発環境構築メモ
- Pythonでオブジェクトの内部構造を知る方法
- HMAC 方式を使用してハッシュ値を生成する。
- MySQLdbでイテレータ
- Bottle を使用したウェブアプリケーション(1)
- IPythonのデバッガー(ipdb)の使い方
- Pythonの例外クラスのインスタンスを直接、例外クラスの引数にするな!
- lambdaを使う例
- ベータ分布をPythonで書く
- Python 3のsorted関数と比較関数
- Pythonでバイナリファイルを作成する
- AKBメンバーのGoogle+ IDを一気に取得する
- サクラエディタでスクリプトをデバッグする
- Python3.3.1 + Bottle でWebアプリケーション(1) – テンプレートエンジンをjinja2に変更する
- botで天気予報をtweet
- 値が空だったら初期化したい(python)
- 【失敗】Stack OverflowのClone AskbotをCentOS6.4にインストール
- redis.pyのConnectionPoolの実装よんだ
- リスト内包表記でifの判定
- Gmailの件名をtwitterに投稿
- 標準入力で受け取ったりとかコマンドライン操作からGmailの送信
- python 現在時刻取得
- scikit-learnのParallelで並列処理
- 時系列顧客ロイヤリティの算出
- matplotlibでグラフを動的に生成して、reporlabでPDFに埋め込む
- Pythonで逆アセンブラする
- OpenCV2.4(+python)を手っ取り早くOS Xにインストールしてサンプルを試してみる
- AKB48 Google+投稿をゲットする
- eggパッケージのインストールとアンインストール
- すぐにできるページランク計算(全行コメント解説つき)
- MeCabをPython3上から使えるようにする
- matplotlibやpylabで論文向きの白黒のグラフをプロットする
- pythonモジュールの一覧表示
- djangoコマンドの二重起動を防止する
- Scipyでの疎行列の扱い
- OptParserの使い方
- pycharmのショートカット
- Pythonで単語の数え上げとかするならCounterを使うと便利なはなし
- 1分でPython&CSSセレクタによるスクレイピングを実現する
- web アプリケーションで matplotlib の図を表示する
- MacでPython環境構築メモ
- pythonのfluentdではまりどこ??
- pythonのSimpleHTTPServer
- fabric でエラー時のロールバック処理をする
- RubyとPythonとPerlで正規表現の速さを比べてみた(2013年版)
- ランダム文字列生成(Python)
- Python for Data Analysis Chapter 2
- Python for Data Analysis Chapter 3
- Python for Data Analysis Chapter 4
- virtualenvごとにPYTHONPATHを切り替える
- Mac(Mountain Lion)にPython環境を構築
- matplotlibのインストール(Python 3.3.2)
- py2exeとsetuptoolsの連携
- Mountain Lion環境への「virtualenv」と「pythonz」のインストール手順 + 利用方法
- urllib.parse.quote関数使用時の注意
- GoogleAppEngine/PythonでDjangoを使う方法
- 三角関数とか使ったメモ
- OSXでのCython実行方法メモ
- 今どきのPythonのライブラリ自作からPyPIへの登録
- 実行時間とかメモリ使用量とか気にしてみようと思ったから
- pythonbrewでPythonインストールしてFlaskをWSGIサーバで動かすまで
- Python で画像フォーマット
- Pythonのクラスで__eq__などを汎用的に実装する
- Mac に matplotlib をインストールする手順
- Python3をさくらサーバー(FreeBSD)にインストール
- ファイルの削除にrmコマンドは使わないようにした
- ruby は インスタンス変数の確認に instance_variable_defined? を使う
- twitter利用時間をを基準にざっくりと推定睡眠時間を計算する
- Djangoのモデルでchoice属性で値を限定しているフィールドの「名前」を取得する方法
- watchmedo(watchdog)を使ってエディタでファイルを保存する度にテストを実行する時の設定
- Celeryの非同期処理中に起きたエラーをメールで通知
- Pythonライブラリのダウンロード数表示バッジを生成
- virtualenvでpython
- randintに気をつけよう
- Pythonで日本語メールを送る方法をいろいろ試した
- MarvericksにしてPILのインストールがコケる件
- Django管理サイトのチューニング
- AnacondaでPython3.3
- MacOSX Mavericks(10.9)にhomebrewを使ってDjangoの開発環境を整える
- Python 3.3でmatplitlibとpylabを使おうとしたら RuntimeError: Python is not installed as a frameworkというエラーが発生したときの解決方法
- GensimPy3を使って小説家になろうのトピックモデルを解析
- `return self`でメソッドチェーン
- Python3.3.1でcChardetとpython3-chardetを使ったメモ
- pythonのデフォルトエンコーディングをutf-8に変更する
- utf-8を含む文字列を含む配列をprintしたときに文字化けする問題
- importについて
- TOPIX の時系列を表示する
- zip関数の挙動を少し試した
- django の dumpdata コマンドの代替
- scikit-learnでCross Validation
- TOPIXの時系列を pickle, csv, Excel形式で保存する
- 機械学習ライブラリ SHOGUN入門
- Python初歩からの学習メモ1
- Python初歩からの学習メモ2
- #python pythonの日本語のシンタックスエラー回避
- python-sphinxで表を書く時は csv-table を使った方が便利
- リスト操作とか競技プログラミングで使ったりした私用メモ
- Python3.4からpipが標準インストーラに!?
- rauthでTwitterAPIにアクセスするのが簡単すぎて、僕にも彼女が…
- ダミーデータファイルを作る
- Mac OS Xで複数バージョンのPythonを利用する(1)複数Verインストール編
- Mac OS Xで複数バージョンのPythonを利用する(2)利用編
- 不均衡データにおけるsampling
- “__slots__“を使ってメモリを節約
- pythonで日時の差分を秒単位で出す方法
- PythonでStateモナド
- 【募集】プログラミングのスキル交換をしませんか
- Supervisorで簡単にデーモン化
- Pythonでlet式を使う
- AWS コマンドラインインターフェイス(Python/awscli)をMac OS Xで利用する手順
- Pythonのパッケージ管理ツールez_setupの覚書
- sitecustomize.py を使わず usercustomize.py を使おうね
- ローカルのGAEを同一LAN内のiPhoneのブラウザから確認する方法
- はじめてのPython 素数に0, 1をくっつけて返すスクリプト
- PythonでCSVの読み書き
- easy_installのインストール
- Pelican ブログのインストール方法
- PythonでNetCDFの読み書き
- テキストファイルから指定した文字列を含む行を出力する
- pythonメモ:easy_installが使えないとき
- ベイズ線形回帰(PRML§3.3)の図版再現
- ラテン語文解析プログラムを書くことを目的としたラテン語学習(前編)
- MongoEngineでMongoDBを触ってみる基礎編
- Translate Toolkitで翻訳ツールを作る
- Pythonでカバレッジを調べる
- たぶん1分くらいでできる形態素解析とtfidf(テストコードつき)
- ChatWork API を叩く PHP と Python のサンプル
- LXCをPythonから操作する
- sympyで運動方程式
- APSchedulerで少し進んだジョブスケジューリング
- IntelliJ IDEAのPythonプラグインからもvirtualenvが使える
- Pythonのクラスメンバのスコープまとめ
- Keynoteに美しくスニペットを貼る
- ラテン語文を合成音声で読み上げる技術
- SphinxでPythonドキュメントを自動的にビルド
- b-Bit MinHashを使ったサイトのカテゴリ分類
- とにかく簡単に JSON データを確認したい
- 第16回オフラインリアルタイムどう書くの問題をPythonで解いてみた
- DJangoメモ:はじめから(準備編)
- Pythonでニュース速報(嫌儲)のスレッド一覧を取得してみる。
- scipyとか使ってみる
- scipyでフィルタ作成
- numpyでハミング符号
- ὑμήνπτερόν
- feedparserで自動的にねこ画像を拾ってくる
- pyOpenGLでシェーダープログラミング
- DJangoメモ:はじめから(モデル設定編)
- DJangoメモ:はじめから(管理画面を使う編) myハマりポイント
- pythonのunittestのassertXXX一覧
- 秒速でねこ画像を集めてネコヒルズ族を目指す
- ScipyでICA
- Docker で Google AppEngine 開発
- 最小お釣問題について考える
- DJangoメモ:はじめから(管理画面をもっと編集編)
- Python でテスト
- AnsibleのPython APIを試す。
- Pythonのインストール(Windows)
- DJangoメモ:はじめから(ビューを作る編)
- ConfigParserモジュールの使い方
- [Python] virtualenvとは何か
- Python の subprocess で出力を受け取るときは communicate() を使おう
- はてブのホッテントリのタイトルを要約してWebの今を見つめる
- DJangoメモ:はじめから(テンプレートからビューを作る編)
- Python3.3でナイーブベイズを実装する
- Python3.3でナイーブベイズを実装する
- 犬派と猫派の勢力図をGoogleChartAPIでグラフ化して決着をつける
- Python3.3で実装したナイーブベイズをBing APIで取得したWebページで学習。文章を分類させる
- 【cocos2d-x 3.0】binding-generatorでScript Bindingを自動化する方法
- iOS実機のSSL通信をプロキシによって傍受したり改ざんする方法
- DJangoメモ:はじめから(エラー画面設定編)
- 少しのコードでWebPayを導入する Python Ver.
- カレントディレクトリをドキュメントルートとしてHTTPサーバを立てる
- DJangoメモ:はじめから(URLConfの単純化と分割)
- AnsibleでPython(正確にはJinja2)の文法を生かしたスマートな条件文の書き方集
- MacOSXにSphinxをインストール
- Ansibleを導入したい人の為のくどきポイント
- DJangoメモ:はじめから(フォーム処理)
- DJangoメモ:はじめから(汎用ビューの使用)
- FlaskとPILImageを使ってリサイズした画像をResponseする
- Python3.3で実装したナイーブベイズ分類器を利用して、文章と文字列中の語の共起頻度から、類似度を計算する
- herokuでDJangoページを公開:準備編 myハマりポイント
- Pythonライブラリ・Botoで素敵にAWSを管理する
- Pythonメモ:pipコマンドが使えないとき
- FlaskからWebPayを導入する
- SublimeTextに実績機能をつけた
- pythonでもっとも簡単に音声合成する方法
- Python Jinja2
- PythonでLeapMotionを使ってみる
- 今週のアルゴリズム:最短経路の計算!(Ruby/Python/C#/VB/Goでpermutation iterator)
- 今週のアルゴリズム:最短経路の計算!(PHP/Python/Ruby/HSPでnext_permutation、Perlでpermutation iterator)
- scikit-learnでtf-idfを計算する
- CMake+SWIGで簡単にC/C++の多言語バインディングを実現する
- JavaScriptでもジェネレーター関数
- Mac OS X 開発環境構築メモ
- pandasメモ
- herokuでDJangoページを公開:実践編
- 30C3 CTF オンライン予選大会の PyExec 問題紹介
- wxPythonをMacOSXにインストールする際に「”〜.pkg”は壊れているため開けません。」のメッセージが出たときの対処法
- python3でtwitter
- Twitterでつぶやく
- Twitter-Pythonの使い方
- numpyで行列の斜めのやつを取得する
- python の map オブジェクトを list にした後は何も残らない
- PythonとPHPで文字列分割をして配列の最後の要素を取得
- Google App Engine for PythonでTweet(API1.1)
- pythonでmarkdownを扱う
- Matplotlibによるヒストグラム透明重ね書き
- Pythonで標準出力をnon-blockingにする
- 1分で実現できる!関数の実行結果をmemcachedにキャッシュするデコレータ
- pythonクイズ
- ワンライナーWebサーバを集めてみた
- X-SendFileを用いて静的ファイルをサーブ
- herokuでDjangoブログ:ログイン実装
- Command Line で使用しているツールまとめ vol.8
- APIでできること vol.1
- 変数の変数名を文字列で取得する。
- pyrtm と RTM CLI を使う
- スコープでリソースを破棄するいろいろなやりかた
- Django:参考資料
- どうやってコードを書けばいいのかな? というときに役立つかもしれない howdoi
- SQLAlchemy で独自の Composite Value を作る
- networkxでグラフを描く
- Twitter の OAuth ログインを自動化するスクリプト
- ファイルをzip圧縮して別のサーバにバックアップする
- boto を使って Amason S3 に指定のフォルダをアップロードするスクリプト
- argparseの使い方とoptparseとの違い
- Coverallsで “coverage unknown” になった時の対処法
- pythonでflatten
- Python 再帰の上限の参照と変更
- pythonでホームディレクトリの取得
- Pythonのスタイルを自動で整形させるVimプラグイン
- Tornado + nginx の時のIPの取得方法
- Python3でImportError: No module named ‘xxxxx’
- ScraperWikiを使ってWEBサイトからデータを定期的に取得する
- Scikit-learnでPCA
- Scikit-learnでIsomap
- メモ:Vagrant環境でCGI(試行錯誤中)
- Python 3.4 から標準ライブラリに入る Enum 型が今からでも便利
- SQLAlchemy で Enum を使う
- Django Templateのif-elseを短く書く
- Macでpyenv+virtualenv
- LINQのPython実装 “linqish”の紹介
- Numpyで行列の連結
- Scipyでデータをnormarizeする
- reportlabで画像追加メモ
- django-celery で Amazon SQS を使ってみた
- 環境構築手順書: Ubuntu + Apache2 + Python + Pyramid
- Path API を叩く PHP / Python / Ruby のサンプル
- Pythonメモ(自分用):配列
- 【python】web開発準備(仮想環境の構築)
- Python版のConfluence API用モジュールを使ってみます。
- PythonでYouTubeの動画を自動的に検索&ダウンロードする
- Pythonメモ:現在の月を取得
- sqlalchemyでgroup_byしてsum
- 僕とcpとSubprocessと
- Pythonの進化計算ライブラリDeap
- Scikit-learnでハイパーパラメータのグリッドサーチ
- 泥沼の記憶(CSV)をワンライナーで処理する
- pipで既にインストールされているパッケージをuser環境(非su)でアップデートする
- 2D plot in matplotlib
- 今日の株式取引は?
- 東証1部上場銘柄の名前と証券コードを取得する
- python で redmine の更新を hipchat へ流す
- Bottle0.13+jPlayer2.5で自分だけのミュージックプレイヤーを作ろう!
- Ruby と Python の仮想環境操作を統一する
- CentOS に Python2.7, Python3を入れたメモ
- anyenvで開発環境を整える
- pythonで文字列ゼロ埋め、文字列からある文字をカウントする
- Pythonで正規表現使うとき
- pythonにおける相対urlの処理
- pythonで文字列が数字かどうかを確認する
- pythonにswitchはないけれど
- Python と node.js のテストライブラリを簡単に比較してみた
- Webアプリ開発実践:Djangoでシフト作成ページを作ろう!(はじめに)
- numpyで作成した行列の可視化
- Pythonメモ:オブジェクトの持つ属性を知りたいとき
- supervisorでulimitを設定する

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。