hiramekunのブログ

プログラミングと読書と

メモリの種類を10分でまとめる(ROM, RAM)

◎ROM

Read Only Mmory
その名の通り基本動作は読み出しのみ。
次のようにROMには様々な種類があります。

○マスクROM

メモリLSI(データを記憶に特化した回路のこと)の設計時に内容が決まる。ユーザーによる消去・書き込みは不可能。

○PROM ROM

Programmable ROM
ユーザーによる消去・書き込みが可能。

ヒューズROM

ユーザーが一度だけ書き込みが可能。以後の消去・書き込みは不可能。

EPROM

Erasable PROM
ユーザーによる消去・書き込みが何度でも可能。

  • UVEPROM(紫外線で消去・書き込み)
  • EEPROM(電気で消去・書き込み)
  • フラッシュメモリ(電気で消去・書き込み。ブロック単位で高速に処理可能)

◎RAM

Random Access Mmory
基本動作は読み出しと書き込み。
次のようにRAMにも様々な種類があります。

SRAM

Static RAM
セルはフリップフロップで、リフレッシュ(後述)不要。
高速で、レジスタファイルやキャッシュメモリなどに用いられる。

DRAM

Dynamic RAM
セルはコンデンサ
コンデンサなので、放置しておくと電荷が流れてしまう。そこで、ある時間ごとに電荷の状態を確認して再び電荷を加え直したりしなければならない。このことをリフレッシュという。

高速モードDRAM

連続する列アクセスを高速化。

SDRAM

Static DRAM
クロック同期とアクセスのオーバーラップによる高速化。

RDRAM

Rambus DRAM
データ幅の縮小と直列接続による高速化。


こちらのpdfファイルを参考にしました
http://www.mtl.t.u-tokyo.ac.jp/~sakai/hard/hard2.pdf

コマンドラインからpickleファイルの中身確認

Pythonファイルでは

pythonではpickle.dump()で書き込み、pickle.load()で読み込みます。

# 書き込み
with open("hoge.pkl", mode="wb") as f:
    pickle.dump({"hoge":"fuga"}, f)

# 読み込み
with open("hoge.pkl", mode="rb") as f:
    hoge = pickle.load(f)

コマンドラインでは

しかし、これでは手軽にpickleファイルの中身を確認することができません。
pythonファイルを別に作ってそれを毎回指定して実行するのも面倒な気がします。

すると、コマンドラインからでも次のようにすれば読み込めるとのこと。

$ python -m pickle hoge.pkl
{hoge: fuga}

これだけ。

こちらの記事を参考にさせていただきました。
qiita.com

python+scikit-learnで多項式曲線近似をリッジ回帰で求める

はじめに

実装はこちらに全て載っています。
github.com

多項式曲線近似とは

多項式曲線近似とは、関数があったとき、それを次のような多項式で近似することです。
{ \displaystyle
y(x, w) = w_0 + w_1x + w_2x^{2} + ... + w_Mx^{M} = \sum_{j=0}^{M} w_jx^{j}
}
ここで、Mはパラメータで、自分で近似する多項式の次数を決定します。一般的に次数が高すぎると過学習してしまい、学習データセットに対する精度は上がりますが、汎化性能については低くなってしまうことが多いです。

これを一般的に、線形モデルと言います。

係数の求め方

ここで、線形モデルの各係数wを求める必要があります。
多項式近似して予測した値と、実際の値との誤差関数を最小化するという方針で求めます。

一般的な回帰モデル

誤差関数は以下が用いられることが多いです。二乗誤差の最小値を求めに行くので、最小二乗法と呼ばれます。

{ \displaystyle
E(w) = \frac{1}{2} \sum_{n=1}^{N} \{y(x_n, w) - t_n\}^{2}
}
ここで、y(x_n, w)多項式によって予測された推定値。t_nは正解の値です。この誤差関数をw_j微分して最小値を求めます。

正則化した回帰モデル

一般的な回帰モデルだと、元の関数から大きくずれることがあります。それは、学習データセットに対して適合し過ぎてしまい、大きい係数が出てきてしまうためです。

そこでリッジ回帰モデルでは、罰則項を付加することにより過学習を防ぎます。この作業は正則化(regularization)と呼ばれています。

具体的には以下のような誤差関数によって回帰モデルを求めます。
最後に係数を二乗で足しているのがわかりますね。
{ \displaystyle
E(w) = \frac{1}{2} \sum_{n=1}^{N} \{y(x_n, w) - t_n\}^{2} + a|w|^{2}
}
ここで、aはパラメータで自分で値を設定します。

実装

元の関数

python+sklearnで実装しました。
元の関数、そしてそこから外れた値を生成するためにrandomな値を加算する関数を定義します。

def function(x):
    return 5 * np.sin(x * np.pi / 10)

def function_with_random(x):
    return function(x) + np.random.uniform(-5.0, 5.0)

多項式の定義

sklearnには、PolynomialFeaturesというクラスがあり、今回6次の多項式を定義しました。

from sklearn.preprocessing import PolynomialFeatures
polynomial_features = PolynomialFeatures(degree=6)
X_poly = polynomial_features.fit_transform(X)

このfit_transform()ですが、これは定義した多項式に対して、引数で与えられたXの値を代入した配列が入っています。今回は以下のようになります。

[[1, 0, 0, 0, 0, 0, 0],  # x = 0
[1, 1, 1, 1, 1, 1, 1, 1],  # x = 1
[1, 2, 4, 8, 16, 32, 64],  # x = 2
...
]

学習

次にやることは、先ほど各 1, x, x^{2}, x^{3}...の値を計算したので、それらの線形結合で予測値を出すということです。

from sklearn.linear_model import Ridge
poly_reg = Ridge(alpha=10, fit_intercept=False)
reg  = poly_reg.fit(X_poly, y_train)  # X_polyの線形結合によりyの値を学習する

予測

seabornを使ってグラフを描画しました。

y_ridge = poly_reg.predict(X_poly)  # 学習したモデルを用いて予測する。
sns.pointplot(x=X.reshape(len(X)), y= y_ridge, color='R', markers="")

赤がリッジ回帰によって得られた曲線。青が比較のために実装した正則化しない線形回帰によって得られた曲線です。
確かに外れ値の値を受けにくいような気がします。
f:id:thescript1210:20180409010132p:plain
モデルの平均二乗誤差は次のようになり、確かにリッジ回帰モデルでは過学習が抑制され、汎化性能を保っていることがわかります。

rigde_train_error: 3.8009207105204244
rigde_test_error: 2.665180445057409
linear_train_error: 1.987241639408757
linear_test_error: 3.6306109275834806

ChainerCVでSSDを学習させる時の注意点

はじめに

この記事は、自分がChainerCVを用いてSSDを学習させる時にハマった点や注意した点を紹介しています。

SSDとは

簡単にSSDの説明をすると、SSDとはSingle Shot MultiBox Detectorの略で、

①VGGをbase networkとして異なる大きさのフィルターをかけていき
②異なるアスペクト比、解像度のbounding boxに対応した
③精度74.3%, 速度59FPSを記録した高い精度で高速

な物体検出方法です。それまでの先行研究に比べて学習時に物体らしさのを物体の分類と同時に学習するために、今までよりも高速な学習が可能になっています。

論文はこちら。
[1512.02325] SSD: Single Shot MultiBox Detector

また、こちらのスライドまとめがSSD含めた物体検出(object detection)の理解に非常に役立ちました。

www.slideshare.net

ChainerCVとは

ChainerCVは、ディープラーニング用ライブラリChainerのコンピュータービジョンに対応したもので、物体検出の手法が数多く実装されています。
GitHub - chainer/chainercv: ChainerCV: a Library for Deep Learning in Computer Vision

参考にした学習コードは、ChainerCVのexamplesにあるtrain.pyです。
github.com

注意点

MultiProcessIteratorのデッドロック

よし、gpuで学習を始めよう!と思うわけですが、学習が一向に進みません。SerialProcellIteratorを用いた時には正常に動くので、MultiProcessIterator特有の挙動のようです。

ChainerCVのissueを漁っていると同じような状況ハマっている方を見つけました。
github.com
どうやらMultiProcessIteratorが内部でcv2.resize()を呼んでおり、これが別スレッドを立てるためにデッドロックに陥るとのことです。

コメント通り、util.pycv2.resize()の直前にcv2.setNumThreads(0)を追加したところ、学習が開始されました。

Validationの適切なログ出力

ここでいう「適切」とは、自身に秘帖なタイミングで適切に出力するように変更しようということです。

デフォルトでは、10,000iterationごとにtestデータセットを用いてvalidation accuracyをlog出力しています。学習を120,000iteration回すので、それでも良いかもしれませんが、自分は頻繁にログを確認したかったので次のように変更しました。

trainer.extend(
        DetectionVOCEvaluator(
            test_iter, model, use_07_metric=True,
            label_names=voc_bbox_label_names_with_hands),
        trigger=(1000, 'iteration'))  # 1000iterationごとにvalidationのログ出力

頻繁にvalidation accuracyを出力する時ですが、かなり時間がかかるのでそこは留意したほうが良いかと思います。

使用できる学習済みモデルはimagenet

ChainerCVで実装されているSSDはVOCデータセットの20クラス分類に対応しています。
ですので、自作のデータセット含めた21クラス分類の転移学習(fine-tuning)がVOCデータセットの重みを用いてできるかと思ったのですが、現状はできません

こちらのissueにもあるように、20クラス分類以外の場合はVOCデータセットの重みは用いることができず、現在実装中のようです。
github.com

自作データセットを用いて20クラス分類以外の学習をさせたい時には、pretrained_modelはimagenetのままで学習させましょう。

if args.model == 'ssd300':
        model = SSD300(
            n_fg_class=21,  # 用途に応じて変更
            pretrained_model='imagenet')
    elif args.model == 'ssd512':
        model = SSD512(
            n_fg_class=21,  # 用途に応じて変更
            pretrained_model='imagenet')

Python3.6で文字列を扱うならf-strings

f-stringsとは

Python3.6から追加された文字列の操作のための新しい記法の一つです。例をあげてみるとこのような感じになります。

name = 'Bob'
age = 20
f"my name is {name}. I am {age} year's old."
# => my name is Bob. I am 20 years's old.

f'文字列'というように、文字列の前にfをつけて宣言します。そして{}の中で動的に変数を展開し、その値を出力しています。

ドキュメントはこちらです。
www.python.org

This PEP is driven by the desire to have a simpler way to format strings in Python.

とあるように、文字列をフォーマットするためのより簡潔な記法を実現しています。 今までだと上記の例でしたら、次のように書く必要があり、format()の分行が長くなります。
さらに、文章が長くなり代入する文字列も増えてくると、{}を発見した後にfomatの中身をみてもう一度{}を確認しに行く...といったコードリーディングの際にも冗長性が発生します。

"my name is {n}. I am {a} year's old.".format(n=name, a=age)

使い方例

こちらの記事を参考にしました。

qiita.com

関数を評価

{}内は動的に評価されるために、関数を書いてその返り値を出力することができます。

def calc_tax(price):
    return price * 1.08

f'This pencil is {calc_tax(100)} yen.'
# => This pencil is 108 yen.

配列アクセス

fruits = ['apples', 'oranges', 'bananas']
f'I like {fruits[0]}'
# => I like apples.

{}のネスト

{}はネスとしてもよく、その都度変数が展開されます。

float_val = 0.123456
size = 4
f'result: {float_val:.{size}}'  
#=> 'result: 0.1235'

注意点

f-strings記法では、バックスラッシュ(\)を入れてはいけません。一番最初にあげた例だと、次のように書くことができそうですが、SyntaxErrorが帰ってきます。

f'my name is {name}. I am {age} year\'s old'
# => SyntaxError: f-string expression part cannot include a backslash

畳み込みニューラルネットワークを用いた文章分類をkerasで実装する

概要

機械学習を用いた文章分類(Text Classification)タスクは、主にRNN(Recurrent Neural Networks)で実装されることが多いです。それは、文章というものの特徴に由来しています。従来のCNNのように入力が独立ではなく、文脈や前後関係があって初めて文章の意味が成立するからです。

しかし近年では文章分類タスクに適していないと考えられていたCNNを用いた文章分類の論文がいくつか出ています。
それに興味を持って実装、検証した結果を書いていきたいと思います。
自分の実装はこちらにアップしています。 github.com

分類対象

分類の対象にしたデータセットは、pubmed-rct PubMed 200k RCT datasetです。
github.com

a Dataset for Sequential Sentence Classification inMedical Abstracts

とあるように、医学分野論文のabstractの文脈中で、20万個ある各文がどのような役割を担っているのかを分類するためのデータセットです。データセットの内容に関する論文はこちらにあります。
[1710.06071] PubMed 200k RCT: a Dataset for Sequential Sentence Classification in Medical Abstracts

headコマンドで一部をのぞいてみると、こんな感じに各ラベルと文がテキストファイルになって入っています。

>> head data/train.txt
OBJECTIVE   To investigate the efficacy of 6 weeks of daily ...
METHODS A total of 125 patients with primary knee OA were ...
METHODS Outcome measures included pain reduction ...
METHODS Pain was assessed using the visual analog pain ...
METHODS Secondary outcome measures included the Western Ontario ...
METHODS Serum levels of interleukin 1 ( IL-1 ) , IL-6 , tumor necrosis factor ...
RESULTS There was a clinically relevant reduction in the intervention ...
RESULTS The mean difference between treatment arms ( 95 % CI ) ...
RESULTS Further , there was a clinically relevant reduction in the serum ...

今回はこのデータセットのうち、学習時間を考慮して文が2万件ほどの縮小版のデータセット、Pub Med 20k RCT datsetを用いました。

実装

kerasのgithubにあった、imdbデータセットをCNNを用いて分類するコードを参考にしました。
keras/imdb_cnn.py at master · keras-team/keras · GitHub

train/test split

元のデータセットでは、trainが180,000件。testが30,000件と、かなり偏った比率になっていたので、一回trainとtestを混ぜ合わせてから2:1に分割し直すことをしました。x_all.reshape((len(x_all)), 1))などとしているのは、tokenizerでベクトルに変換した場合はshapeが(300, )のようになっているのに対して、train_test_split()では(300, 1)としないとダメなためです。

# data_loader.py

from sklearn.model_selection import train_test_split
# 中略
x_train, x_test, y_train, y_test = train_test_split(x_all.reshape((len(x_all), 1)),
                                                       y_all.reshape(
                                                           (len(y_all[:]), len(y_all[0][:]), 1)),
                                                       test_size=0.33,
                                                       random_state=0)

ベクトル変換

文字列を機械学習で扱うためには、まずは文字をベクトル表現しなければなりません。kerasのTokenizerを用いて、fit_on_tests→texts_to_sequencesでベクトル表現に変換します。

# data_loader.py

from keras.preprocessing.text import Tokenizer
tokenizer_train = Tokenizer(filters="")
tokenizer_test = Tokenizer(filters="")

tokenizer_train.fit_on_texts(x_train)
tokenizer_test.fit_on_texts(x_test)

x_train = tokenizer_train.texts_to_sequences(x_train)
x_test = tokenizer_test.texts_to_sequences(x_test)

ラベル

categorycal_crossentropyを損失関数に使いたいので、今回はラベルをone-hot表現にする必要があります。 例えば、(りんご, バナナ, もも)があった時に、[2]と表現して配列の3番目の"もも"を表しているとします。これはone-hot表現にすると、[0, 0, 1]となります。つまりラベルの順番での表現から、「表したいラベルを1、それ以外を0」として表現するということです。

kerasの to_categorical で変換してくれます。

# data_loader.py

from keras.utils.np_utils import to_categorical
categorical_labels = to_categorical(whole_labels, num_classes=5)

train.py

そして、変換したベクトルを埋め込み表現にします。kerasのEmbeddingでそれを実装できます。

  • input_dim: 単語の数
  • output_dim: 埋め込みの次元
  • input_length: 入力配列の長さ
# imdb_sample.py

embedding_dims = 128
vocab_size_train = len(tokenizer_train.word_index) + 1
X_train = sequence.pad_sequences(X_train, padding='post')  # 一番長い文に合わせてベクトルの長さを合わせます
seqX_len = len(X_train[0])

model = Sequential()
model.add(Embedding(input_dim=vocab_size_train, output_dim=embedding_dims, input_length=seqX_len))

学習

学習率が0.001がデフォルトでしたが、学習がうまく進みませんでした。そこで徐々に小さくしていくと、0.00001でようやく学習がうまく進むようになりました。

optimizer = Adam(lr=0.000001)

こちらの記事などを参考にして、Google ColaboratoryでGPUを使いました。
qiita.com

20epoch回した様子です。 f:id:thescript1210:20180326113813p:plain
f:id:thescript1210:20180326113817p:plain

結果

最終的には、validation_loss 1.062, validation_accuracy 57.4%となりました。abstractの文脈を無視して一つ一つの文単体のみで分類を行ったからでしょうか、あまり精度は高く出ていないのが残念です。
より良い分類の実装ができたらまた書きたいと思います、ありがとうございました。

再帰的にmkdirする(Linux, Python)

Linuxコマンドでは

mkdir -pコマンドで、再帰的に深い階層のディレクトリまで作成してくれます。
また、ディレクトリの中を再帰的に表示する場合は、ls -Rで表示することができます。

[/Users/hiramekun/sample]
>> mkdir -p a/b/c
[/Users/hiramekun/sample]
>> ls -R
.  .. a

./a:
.  .. b

./a/b:
.  .. c

./a/b/c:
.  ..

Pythonでは

os.makedirs()によって再帰的に作成することができます。

import os
os.makedirs(os.path.join('a', 'b', 'c'))