WHAT'S UP?

New post every sometimes. Here's OKKAH NET.

IEEE BigData 2019 @ ロサンゼルスで学会発表してきた

12月9日〜12日に開催されたIEEE BigData 2019に参加してきました。

bigdataieee.org


IEEE BigData 2019
IEEE BigDataはビッグデータに関する国際学会です。
基礎研究から応用研究、ビジネス適用事例まで幅広く取り扱われ、システム基盤からデータマイニング、セキュリティやアプリケーションなど様々なトピックの論文が発表されています。

今年の開催地はロサンゼルスでした。
ロサンゼルスはカリフォルニア州にある大都市で、アメリカの映画・テレビ産業の中心地です。
成田から飛行機で約10時間、日本との時差は17時間で日本の方が先に進んでいます。
12月なのに最高気温は18℃前後で最低気温は10℃程度、日本の春のようなとても過ごしやすい気候でした。

f:id:okkah:20191229012045j:plain

f:id:okkah:20191229012804j:plain

ロサンゼルス


自分はIEEE BigData 2019のThe 6th International Workshop (KDDBHI 2019) on Big Data Analytic Technology for Bioinformatics and Health Informaticsで発表しました。
研究者と臨床医が意見を交換し、バイオインフォマティクス、精密医療、個別化医療ビッグデータ分析、データサイエンス、そしてAI、これらの融合を促進させることを目標として掲げられています。

発表した論文はStochastic Gastric Image Augmentation for Cancer Detection from X-ray Imagesです。
臨床現場におけるX線画像からの胃癌自動検出のために効果的なonline data augmentation手法を提案しました。
具体的には学習前に胃X線画像の「ひだ」のみを確率的に強調し、F1スコアの検出性能を6.9%向上させました。

今回が初めての国際学会発表ということもあり、非常に多くの刺激と学びがありました。
オーラル発表は20分間と少し長く感じましたが、ちゃんと準備すれば意外となんとかなることを実感しました。

f:id:okkah:20191229011819j:plain

発表風景


f:id:okkah:20191229012216j:plain

バンケット(宴会)



観光編


ビバリーヒルズにあるロデオドライブ。超有名ブランド店が軒を連るリッチな雰囲気...!



カリフォルニア大学ロサンゼルス校 UCLAを満喫。



世界一有名な歩道とも言われるウォークオブフェームを見にハリウッドへ。



ロサンゼルス観光といえばやっぱりハリウッドサイン。



サンタモニカビーチでは大衆の前でプロポーズするカップルを発見...!



グリフィス天文台でロサンゼルスの夜景を一望。



In-N-Out Burger、そしてステーキ&ロブスター、これぞアメリカン。



移動は主にLyftを使って。タクシーよりも安く移動できる配車サービスです。



また国際学会来れるように研究頑張ります。

AtCoder Beginners SelectionをPythonで解いてみた

AtCoderはオンラインで参加できるプログラミングコンテスト(競技プログラミング)のサイトです。
リアルタイムのコンテストで競い合ったり、約3000問のコンテストの過去問にいつでも挑戦することができます。

AtCoder Beginners Selectionとは

atcoder.jp

このコンテストは「AtCoderに登録したけど何をしていいか分からない...!」という人に向けて作られた初心者向け問題集です。
AtCoder に登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~ - Qiitaの記事で選出された問題が使用されています。

今回はこのコンテストをPythonで解いてみました。

第0問 PracticeA - Welcome to AtCoder

【問題文】
高橋君はデータの加工が行いたいです。
整数a, b, cと、文字列sが与えられます。a+b+cの計算結果と、文字列sを並べて表示しなさい。

【制約】
・1 ≤ a, b, c ≤ 1000
・1 ≤ |s| ≤ 100

【入力例】
1
2 3
test

【出力例】
6 test




【ポイント】
・1行/1列, 1行/C列の入力処理
・四則演算

【解法】

a = int(input())
b, c = map(int, input().split())
s = input()
print("{} {}".format(a+b+c, s))



第1問 ABC086A - Product

【問題文】
シカのAtCoDeerくんは二つの正整数a, bを見つけました。aとbの積が偶数か奇数か判定してください。

【制約】
・1 ≤ a, b ≤ 10000
・a, bは整数

【入力例1】
3 4

【出力例1】
Even

【入力例2】
1 21

【出力例2】
Odd




【ポイント】
・倍数判定

【解法】

a, b = map(int, input().split())
print('Odd' if a*b % 2 else 'Even')



第2問 ABC081A - Placing Marbles

【問題文】
すぬけ君は1, 2, 3の番号がついた3つのマスからなるマス目を持っています。各マスには'0'か'1'が書かれており、マスiにはsiが書かれています。
すぬけ君は'1'が書かれたマスにビー玉を置きます。ビー玉が置かれるマスがいくつあるか求めてください。

【制約】
・s1, s2, s3は'1'あるいは'0'

【入力例】
101

【出力例】
2




【ポイント】
・for文を使わないレベルの全探索
・string型

【解法】

print(input().count('1'))



第3問 ABC081B - Shift only

【問題文】
黒板に N 個の正の整数A1, … , ANが書かれています.
すぬけ君は,黒板に書かれている整数がすべて偶数であるとき,次の操作を行うことができます.
 ・黒板に書かれている整数すべてを,2で割ったものに置き換える.
すぬけ君は最大で何回操作を行うことができるかを求めてください.

【制約】
・1 ≤ N ≤ 200
・1 ≤ Ai ≤ 10^9

【入力例】
3
8 12 40

【出力例】
2




【ポイント】
・for文を用いた全探索
・シミュレーション

【解法1】

#「全て偶数である限り、全て2で割る」をシミュレート
n = int(input())
a = list(map(int, input().split()))
ans = 0
while all(i%2 == 0 for i in a):
  a = [i/2 for i in a]
  ans += 1
print(ans)

【解法2】

#最大で何回2で割れるかを求め、その最小値をとる
n = int(input())
a = list(map(int, input().split()))
ans = float('inf')
for i in a:
  ans = min(ans, len(bin(i)) - bin(i).rfind('1') - 1)
print(ans)



第4問 ABC087B - Coins

【問題文】
あなたは、500円玉をA枚、100円玉をB枚、50円玉をC枚持っています。これらの硬貨の中から何枚かを選び、合計金額をちょうどX円にする方法は何通りありますか。
同じ種類の硬貨どうしは区別できません。2通りの硬貨の選び方は、ある種類の硬貨についてその硬貨を選ぶ枚数が異なるとき区別されます。

【制約】
・0 ≤ A, B, C ≤ 50
・A+B+C ≥ 1
・50 ≤ X ≤ 20,000
・A, B, Cは整数である
・Xは50の倍数である

【入力例】
2
2
2
100

【出力例】
2




【ポイント】
・R行/1列の入力処理
・二重三重for 文を用いた全探索

【解法】

a, b, c, x = [int(input()) for i in range(4)]
ans = 0
for i in range(a+1):
  for j in range(b+1):
    for k in range(c+1):
      if i*500 + j*100 + k*50 == x:
        ans += 1
print(ans)



第5問 ABC083B - Some Sums

【問題文】
1以上N以下の整数のうち、10進法での各桁の和がA以上B以下であるものの総和を求めてください。

【制約】
・1 ≤ N ≤ 10^4
・1 ≤ A ≤ B ≤ 36
・入力はすべて整数である

【入力例】
20 2 5

【出力例】
84

20以下の整数のうち、各桁の和が2以上5以下なのは、2, 3, 4, 5, 11, 12, 13, 14, 20です。これらの合計である84を出力します。




【ポイント】
・整数の10進法表記の扱い方

【解法】

n, a, b = map(int, input().split())
ans = 0
for i in range(n+1):
  if a <= sum(list(map(int, list(str(i))))) <= b:
    ans += i
print(ans)



第6問 ABC088B - Card Game for Two

【問題文】
N枚のカードがあります. i枚目のカードには, aiという数が書かれています.
AliceとBobは, これらのカードを使ってゲームを行います. ゲームでは, AliceとBobが交互に1枚ずつカードを取っていきます. Aliceが先にカードを取ります.
2人がすべてのカードを取ったときゲームは終了し, 取ったカードの数の合計がその人の得点になります. 2人とも自分の得点を最大化するように最適な戦略を取った時, AliceはBobより何点多く取るか求めてください.

【制約】
・Nは1以上100以下の整数
・ai (1 ≤ i ≤ N) は1以上100以下の整数

【入力例】
2
3 1

【出力例】
2




【ポイント】
・ソート
・Greedyアルゴリズム

【解法】

n = int(input())
a = sorted(list(map(int, input().split())), reverse=True)
print(sum(a[::2]) - sum(a[1::2]))



第7問 ABC085B - Kagami Mochi

【問題文】
X段重ねの鏡餅 (X ≥ 1) とは、X枚の円形の餅を縦に積み重ねたものであって、どの餅もその真下の餅より直径が小さい(一番下の餅を除く)もののことです。例えば、直径10、8、6センチメートルの餅をこの順に下から積み重ねると3段重ねの鏡餅になり、餅を一枚だけ置くと1段重ねの鏡餅になります。
ダックスフンドのルンルンはN枚の円形の餅を持っていて、そのうちi枚目の餅の直径はdiセンチメートルです。これらの餅のうち一部または全部を使って鏡餅を作るとき、最大で何段重ねの鏡餅を作ることができるでしょうか。

【制約】
・1 ≤ N ≤ 100
・1 ≤ di ≤ 100
・入力値はすべて整数である。

【入力例】
4
10
8
8
6

【出力例】
3

直径10、8、6センチメートルの餅をこの順に下から積み重ねると3段重ねの鏡餅になり、これが最大です。




【ポイント】
・集合 (set)

【解法】

n = int(input())
print(len(set([int(input()) for i in range(n)])))



第8問 ABC085C - Otoshidama

【問題文】
日本でよく使われる紙幣は、10000円札、5000円札、1000円札です。以下、「お札」とはこれらのみを指します。
青橋くんが言うには、彼が祖父から受け取ったお年玉袋にはお札がN枚入っていて、合計でY円だったそうですが、嘘かもしれません。このような状況がありうるか判定し、ありうる場合はお年玉袋の中身の候補を一つ見つけてください。なお、彼の祖父は十分裕福であり、お年玉袋は十分大きかったものとします。

【制約】
・1 ≤ N ≤ 2000
・1000 ≤ Y ≤ 2 × 10^7
・Nは整数である。
・Yは1000の倍数である。

【入力例】
9 45000

【出力例】
4 0 5

お年玉袋に10000円札4枚と1000円札5枚が入っていれば、合計枚数が9枚、合計金額が45000円になります。5000円札9枚という可能性も考えられるため、'0 9 0'も正しい出力です。




【ポイント】
・計算量を考慮した全探索

【解法】

n, y = map(int, input().split())
for i in range(n+1):
  for j in range(n-i+1):
    if i*10000 + j*5000 + (n-i-j)*1000 == y:
      print(i, j, n-i-j)
      exit()
print('-1 -1 -1')



第9問 ABC049C - 白昼夢 / Daydream

【問題文】
英小文字からなる文字列Sが与えられます。Tが空文字列である状態から始め、以下の操作を好きな回数繰り返すことでS=Tとすることができるか判定してください。
 ・Tの末尾に 'dream' 'dreamer' 'erase' 'eraser' のいずれかを追加する。

【制約】
・1 ≦ |S| ≦ 10^5
・Sは英小文字からなる。

【入力例1】
erasedream

【出力例1】
YES

【入力例2】
dreamerer

【出力例2】
NO




【ポイント】
・Greedyアルゴリズム
・prefix

【解法】

s = input().replace('eraser', '').replace('erase', '').replace('dreamer', '').replace('dream', '')
print('NO' if s else 'YES')



第10問 ABC086C - Traveling

【問題文】
シカのAtCoDeerくんは二次元平面上で旅行をしようとしています。 AtCoDeerくんの旅行プランでは、時刻0に点(0, 0)を出発し、1以上N以下の各iに対し、時刻tiに点(xi, yi)を訪れる予定です。
AtCoDeerくんが時刻tに点(x, y)にいる時、時刻t+1には点(x+1, y), (x−1, y), (x, y+1), (x, y−1) のうちいずれかに存在することができます。その場にとどまることは出来ないことに注意してください。AtCoDeerくんの旅行プランが実行可能かどうか判定してください。

【制約】
・1 ≤ N ≤ 10^5
・0 ≤ xi ≤ 10^5
・0 ≤ yi ≤ 10^5
・1 ≤ ti ≤ 10^5
・ti < ti+1 (1 ≤ i ≤ N−1)
・入力は全て整数

【入力例1】
2
3 1 2
6 1 1

【出力例1】
Yes

例えば、(0, 0), (0, 1), (1, 1), (1, 2), (1, 1), (1, 0), (1, 1)と移動すればよいです。

【入力例2】
1
2 100 100

【出力例2】
No




【ポイント】
パリティ

【解法】

n = int(input())
for i in range(n):
  t, x, y = map(int, input().split())
  if (x+y) > t or (x+y+t) % 2:
    print('No')
    exit()
print('Yes')

Python+OpenCVでラベリング

Python+OpenCVを利用したラベリングについてです。
Pythonのバージョンは3.6.4、OpenCVのバージョンは3.4.1です。


ラベリングとは
二値化画像の白(または黒)の部分において、連続した画素に同じ番号を割り振る処理をラベリングと言います。
通常、同じ番号ごとの幅、高さ、面積(画素数)、重心などの特徴量を求め、オブジェクトの分類や欠陥検査などに利用します。

ラベリングの詳しい説明はこちら。
ラベリング処理アルゴリズム 画像処理ソリューション


OpenCVのラベリング関数
OpenCVのラベリング関数は2種類あります。

・簡易版
ラべリング画像のみを出力する簡易版です。

# ラベリング(簡易版)
nLabels, labelImages = cv2.connectedComponents(image_src)

・nLabels: ラベル数(ただし背景のラベル0もカウントされているため、オブジェクトの数はnLabels - 1)
・labelImages: ラベル番号が入った配列データ(座標 (x, y) のラベル番号は、labelImages[x, y] で参照できる)
・image_src: 二値画像の入力配列


・詳細版
簡易版に加えて、オブジェクトの外接矩形の情報(x, y, width, height)、面積、重心座標も出力する詳細版です。

# ラベリング(詳細版)
nLabels, labelImages, data, center = cv2.connectedComponentsWithStats(image_src)

・nLabels: ラベル数(ただし背景のラベル0もカウントされているため、オブジェクトの数はnLabels - 1)
・labelImages: ラベル番号が入った配列データ(座標 (x, y) のラベル番号は、labelImages[x, y] で参照できる)
・data: オブジェクトの詳細の配列データ(x, y, w, h, size = data[ラベル番号] で参照できる。w: width, h: height, size: 面積)
・center: オブジェクトの重心座標 (x, y) (浮動小数点数)
・image_src: 二値画像の入力配列


実際にラベリングしてみる
入力画像
f:id:okkah:20180802161057j:plain


・簡易版

import cv2
import numpy as np
import random
import sys

# 画像の読み込み
img = cv2.imread('sample.jpg')

# グレースケール化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 大津の二値化
gray = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

# 白黒反転
gray = cv2.bitwise_not(gray)

# ラベリング処理(簡易版)
n, label = cv2.connectedComponents(gray)

# ラベリング結果書き出し準備
color_src = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
height, width = gray.shape[:2]
colors = []

for i in range(1, n + 1):
    colors.append(np.array([random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)]))

# ラベリング結果を表示
# 各オブジェクトをランダム色でペイント
for y in range(0, height):
    for x in range(0, width):
        if label[y, x] > 0:
            color_src[y, x] = colors[label[y, x]]
        else:
            color_src[y, x] = [0, 0, 0]

# オブジェクトの総数を黄文字で表示
cv2.putText(color_src, str(n - 1), (70, 70), cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 255))

# 画像の保存
cv2.imwrite('sample_label1.jpg', color_src)


二値化画像
f:id:okkah:20180802161131j:plain

ラベリング画像(簡易版)
f:id:okkah:20180802161312j:plain


・詳細版

import cv2
import numpy as np
import sys

# 画像の読み込み
img = cv2.imread('sample.jpg')

# グレースケール化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 大津の二値化
gray = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

# 白黒反転
gray = cv2.bitwise_not(gray)

# ラベリング処理(詳細版)
label = cv2.connectedComponentsWithStats(gray)

# オブジェクト情報を項目別に抽出
n = label[0] - 1
data = np.delete(label[2], 0, 0)
center = np.delete(label[3], 0, 0)

# ラベリング結果書き出し用に二値画像をカラー変換
color_src = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)

# オブジェクト情報を利用してラベリング結果を表示
for i in range(n):
    # 各オブジェクトの外接矩形を赤枠で表示
    x0 = data[i][0]
    y0 = data[i][1]
    x1 = data[i][0] + data[i][2]
    y1 = data[i][1] + data[i][3]
    cv2.rectangle(color_src, (x0, y0), (x1, y1), (0, 0, 255))

    # 各オブジェクトのラベル番号と面積に黄文字で表示
    cv2.putText(color_src, "ID: " +str(i + 1), (x0, y1 + 15), cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 255))
    cv2.putText(color_src, "S: " +str(data[i][4]), (x0, y1 + 30), cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 255))

    # 各オブジェクトの重心座標をに黄文字で表示
    cv2.putText(color_src, "X: " + str(int(center[i][0])), (x1 - 10, y1 + 15), cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 255))
    cv2.putText(color_src, "Y: " + str(int(center[i][1])), (x1 - 10, y1 + 30), cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 255))

# 画像の保存
cv2.imwrite('sample_label2.jpg', color_src)


ラベリング画像(詳細版)
f:id:okkah:20180802161435j:plain

DLLAB DAY 2018 ハッカソンに参加してきた

MicrosoftとPreferred Networksが生んだ、日本で最も勢いがあるディープラーニングコミュニティであるDEEP LEARNING LAB主催のDLLAB DAY 2018 ハッカソンに参加してきました。

f:id:okkah:20180621232852p:plain

dllab.ai


ハッカソンの内容
Computer Visionのコースと、Natural Language Processingのコースがありましたが、自分はCVの方に参加しました。

与えられたデータセットを用いて、9種類のクラスに分けられる約1万5千枚のポテトチップスの写真を自動で判別できるようなモデルを構築することが課題でした。

画像のサンプルはこんな感じです。

f:id:okkah:20180621221709j:plain

個人的にはのりしおがなかったのが残念笑


学んだ内容
自分が今回のハッカソンを通じて特に学んだ内容は、

1. Dockerはどこでも誰でも同じ環境が作れて便利
2. まだまだデータの読み込みの練習が必要

ということでした。順番に内容を追っていきます。


1. Dockerについて
Dockerは、アプリケーションを構築、展開、実行するためのオープンなプラットフォームです。

今回は、ユーザーが作成したコンテナをアップロードして公開・共有できるサービスであるDocker Hubと、クラウドコンピューティングプラットフォームであるMicrosoft Azureを使用しました。

Docker Hubにある今回のデータセットが入ったコンテナをMicrosoft Azure上に構成し、これをdocker pullコマンドでローカルにプルし、 docker runコマンドで8888番ポートを使ってコンテナを起動しました。

これにより、ブラウザ上でポートにアクセスすることで、Jupyter Notebookによるコーディングができるようになりました。

Dockerによる利点は、

1. コード化されたファイルを共有することで、どこでも誰でも同じ環境が作れる
2. 作成した環境を配布しやすい
3. 安定した開発を進めたり、リリースサイクルの改善にも役立つ

ということを学びました。


2. データの読み込み
今回は、環境構築とデータの読み込みにかなり時間がかかってしまったため、モデル構築や、前処理にかけられる時間が随分と減ってしまいました。

環境構築は運営側の問題でもあったため仕方がなかったとしても、データの読み込みはもっと勉強する必要があると感じました。

from PIL import Image
import os
import numpy as np
import pickle

# train
img_shape=(224,224)
imgs = []
for filename in os.listdir("../data/train/images"):
    img = Image.open("../data/train/images/" + filename)
    img = img.resize(img_shape)
    img = np.asarray(img, dtype='f')
    img /= 255
    img = np.transpose(img, (2, 0, 1))
    imgs.append((filename, img))
    
labels = {}
with open("../data/train/train_labels.txt", "r") as f:
    for line in f:
        labels[line.split()[0]] = line.split()[1]    

train = []
for fn, img in imgs:
    train.append((img, np.int8(labels[fn])))

with open("traindata.pickle", "wb") as f:
    pickle.dump(train, f)


今回はチームメイトの力を借りつつ、Python標準ライブラリにあるpickleモジュールを用いたオブジェクトの直列化・非直列化について学びました。

直列化 (Serialize) はプログラミングにおいてオブジェクトをバイト列に変換することであり、非直列化 (Deserialize) はその逆で、バイト列を元のオブジェクトを復元することです。

これにより、バイト列に変換されたデータをファイルの形で永続化することができます。

モジュール名でもあるpickleという名前は、ハンバーガーにもはさまっているあいつが由来みたいです。

メモリ上にある揮発性で賞味期限の短いオブジェクトを、バイト列の形でファイルに長期保存できる状態にすることをピクルス、つまり漬物に見立てているのだと考えられます。

Chainer公式サンプルMNISTを読み解いてみた

train_mnist.pyのコードを追ってみました。Chainerのバージョンは4.0.0です。

github.com


MNISTとは
手書き数字の画像セット。機械学習の分野で最も有名なデータセットの1つ。
データセットは70000枚。(訓練データ: 60000枚、テストデータ: 10000枚)


処理の流れ
main関数における大まかな処理の流れは
 ⑴ モデルの生成
 ⑵ Optimizerの生成
 ⑶ Iteratorの生成
 ⑷ Trainer、Updaterの生成
 ⑸ Extensionsの登録
 ⑹ 学習ループ
といった感じです。


Trainerについて
Trainerは学習に必要なもの全てをひとまとめにする機能を持っています。
全体図は以下のようになっています。

f:id:okkah:20180501193156p:plain

基本的にTrainerは、渡されたUpdater(必要ならばExtensionsも)を実行するだけですが、
Updaterは中にIteratorとOptimizerを持っています。

Iteratorはデータセットにアクセスする機能を持ち、
Optimizerは重み・バイアスを更新する機能を持ちます。

つまりUpdaterは
 ⑴ データセットからデータを取り出す。(Iterator)
 ⑵ モデルに渡してロスを計算する。(Model = Optimizer.target)
 ⑶ モデルのパラメータを更新する。(Optimizer)
といった一連の学習の主要部分を担っています。

Extensionsは、可視化やログの保存などの様々な便利な機能を持つので、
必要に応じて使いましょう。


実際にMNISTのサンプルコードを追ってみる
1. 必要なライブラリやモジュールをimport

from __future__ import print_function

import argparse

import chainer
import chainer.functions as F
import chainer.links as L
from chainer import training
from chainer.training import extensions

・__future__: Python2系とは互換性の無い3系の機能を、2系でも使用可能にする。
・argparse: コマンドライン引数を扱えるようにする。
・chainer.functions: パラメータを持たない関数。
・chainer.links: パラメータを持つ関数。
・training: trainer


2. モデルの生成

# Set up a neural network to train    
# Classifier reports softmax cross entropy loss and accuracy at every    
# iteration, which will be used by the PrintReport extension below.    
model = L.Classifier(MLP(args.unit, 10))    
if args.gpu >= 0:
    # Make a specified GPU current
    chainer.backends.cuda.get_device_from_id(args.gpu).use()
    model.to_gpu()  # Copy the model to the GPU
# Network definition
class MLP(chainer.Chain):

    def __init__(self, n_units, n_out):
        super(MLP, self).__init__()
        with self.init_scope():
            # the size of the inputs to each layer will be inferred
            self.l1 = L.Linear(None, n_units)  # n_in -> n_units
            self.l2 = L.Linear(None, n_units)  # n_units -> n_units
            self.l3 = L.Linear(None, n_out)  # n_units -> n_out

    def __call__(self, x):
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(h1))
        return self.l3(h2)

model: Classifierインスタンス。このmodelにデータを渡すと順伝播が始まる。
MLP: レイヤー構成を定義するクラス。
・chainer.Chain: パラメータを持つlinksをまとめておくクラス。
 Optimizerが更新するパラメータを簡単に取得できるように一箇所にまとめる。
・__init__: モデルを構成するレイヤーを定義するメソッド。インスタンス化。
・__call__: クラスのインスタンスを関数として呼び出すメソッド。
L.Linear: 全結合層を実現するクラス。
 データがその層に入力されると、必要な入力ユニット数を自動的に計算し、
 (n_in) x (n_units)の大きさの行列を生成し、パラメータとして保持する。
・relu: 活性化関数(→ゼロから作るDeep Learning 3章)

モデルのインスタンスは以下のようになります。
f:id:okkah:20180501230144p:plain

3. コマンドライン引数の設定

    parser = argparse.ArgumentParser(description='Chainer example: MNIST')
    parser.add_argument('--batchsize', '-b', type=int, default=100,
                        help='Number of images in each mini-batch')
    parser.add_argument('--epoch', '-e', type=int, default=20,
                        help='Number of sweeps over the dataset to train')
    parser.add_argument('--frequency', '-f', type=int, default=-1,
                        help='Frequency of taking a snapshot')
    parser.add_argument('--gpu', '-g', type=int, default=-1,
                        help='GPU ID (negative value indicates CPU)')
    parser.add_argument('--out', '-o', default='result',
                        help='Directory to output the result')
    parser.add_argument('--resume', '-r', default='',
                        help='Resume the training from snapshot')
    parser.add_argument('--unit', '-u', type=int, default=1000,
                        help='Number of units')
    parser.add_argument('--noplot', dest='plot', action='store_false',
                        help='Disable PlotReport extension')
    args = parser.parse_args()

    print('GPU: {}'.format(args.gpu))
    print('# unit: {}'.format(args.unit))
    print('# Minibatch-size: {}'.format(args.batchsize))
    print('# epoch: {}'.format(args.epoch))
    print('')

argparseモジュールを使うと、コマンドライン引数を扱うことができます。
これにより、Pythonを実行するときに様々なパラメータを指定することができます。

例)

$ python train_mnist.py -g 0 -e 10
GPU: 0
# unit: 1000
# Minibatch-size: 100
# epoch: 10

GPU: 0 (0: GPUを使用、-1: CPUを使用)
epoch: 10 (学習の反復回数: 10回)
で実行することができます。(他はデフォルトのまま)



4. Optimizerの生成

    # Setup an optimizer
    optimizer = chainer.optimizers.Adam()
    optimizer.setup(model)

モデルによって順伝播・逆伝播が実行され勾配が計算されますが、
その値を重み・バイアスに反映するのがOptimizerの仕事です。
役割上、モデルをラップするような形になります。

・Adam: パラメータの更新(→ゼロから作るDeep Learning 6章)


5. Iteratorの生成

    # Load the MNIST dataset
    train, test = chainer.datasets.get_mnist()

    train_iter = chainer.iterators.SerialIterator(train, args.batchsize)
    test_iter = chainer.iterators.SerialIterator(test, args.batchsize,
                                                 repeat=False, shuffle=False)

Iteratorでデータセットにアクセスします。

・get_mnist: mnistのデータをtrainとtestに入れる。
・SerialIterator: データを順番に取り出すもっともシンプルなIterator
・batchsize: 何枚の画像データを一括りにして取り出すかどうか。
・repeat: 何周もデータを繰り返し読むかどうか。
・shuffle: 取り出すデータの順番をepochごとにランダムに変更するかどうか。

ここまでが初期化に関する処理です。


6. Trainer、Updaterの生成

    # Set up a trainer
    updater = training.updaters.StandardUpdater(
        train_iter, optimizer, device=args.gpu)
    trainer = training.Trainer(updater, (args.epoch, 'epoch'), out=args.out)

ここから学習ループに関する処理に入ります。

・StandardUpdater: Updaterの処理を遂行するための最もシンプルなクラス。
・args.epoch: stop_trigger。学習をどのタイミングで終了するか。
・out: Extensionsで描画したグラフ画像の保存先。


7. Extensionsの登録

    # Evaluate the model with the test dataset for each epoch
    trainer.extend(extensions.Evaluator(test_iter, model, device=args.gpu))

    # Dump a computational graph from 'loss' variable at the first iteration
    # The "main" refers to the target link of the "main" optimizer.
    trainer.extend(extensions.dump_graph('main/loss'))

    # Take a snapshot for each specified epoch
    frequency = args.epoch if args.frequency == -1 else max(1, args.frequency)
    trainer.extend(extensions.snapshot(), trigger=(frequency, 'epoch'))

    # Write a log of evaluation statistics for each epoch
    trainer.extend(extensions.LogReport())

    # Save two plot images to the result dir
    if args.plot and extensions.PlotReport.available():
        trainer.extend(
            extensions.PlotReport(['main/loss', 'validation/main/loss'],
                                  'epoch', file_name='loss.png'))
        trainer.extend(
            extensions.PlotReport(
                ['main/accuracy', 'validation/main/accuracy'],
                'epoch', file_name='accuracy.png'))

    # Print selected entries of the log to stdout
    # Here "main" refers to the target link of the "main" optimizer again, and
    # "validation" refers to the default name of the Evaluator extension.
    # Entries other than 'epoch' are reported by the Classifier link, called by
    # either the updater or the evaluator.
    trainer.extend(extensions.PrintReport(
        ['epoch', 'main/loss', 'validation/main/loss',
         'main/accuracy', 'validation/main/accuracy', 'elapsed_time']))

    # Print a progress bar to stdout
    trainer.extend(extensions.ProgressBar())

・Extensions: 可視化やログの保存などの様々な便利な機能を持つ。
 ・Evaluator: モデルの評価。
 ・dump_graph: グラフの保存。
 ・snapshot: Trainerを保存。
 ・LogReport: ログを保存。
 ・PlotReport: ロスを可視化して保存。
 ・PrintReport: ログを出力。
 ・ProgressBar: 学習の進行状況を出力。


8. 最後に

    if args.resume:
        # Resume from a snapshot
        chainer.serializers.load_npz(args.resume, trainer)

    # Run the training
    trainer.run()


if __name__ == '__main__':
    main()

・serializers.load_npz: snapshotから再開。
・trainer.run: 学習を開始。
・if __name__ == '__main__':
 直接実行されていればTrue、importされて実行されていればFalse。

ゼロから作るDeep Learning学習備忘録(5〜8章)

ゼロから作るDeep Learning学習備忘録(1〜4章) - OKKAH NETの続きです。


5章 誤差逆伝播
順伝播: 入力→出力へと計算 ・逆伝播: 出力→入力へと計算
誤差逆伝播法(Backpropagation): 損失関数を誤差と見立てて、
 逆伝播によって重みとバイアスを修正していくアルゴリズム
 連鎖律で逆伝播を行い、最急降下法で誤差を最小にする。
 ・連鎖律(chain rule): 合成関数の微分はそれぞれの導関数の積で与えられる。
 ・最急降下法(gradient discent): 関数の傾きのみから関数の最小値を探索する。
勾配確認: 数値微分誤差逆伝播法の結果を比較することで、
 誤差逆伝播法の実装に謝りがないことを確認できる。


6章 学習に関するテクニック
・パラメータの更新: 4章のSGD(確率的勾配降下法)の欠点を改善する手法3つ。
 ・Momentum: 慣性力を利用。関連性のある方向へSGDを加速させ振動を抑制する。
 ・AdaGrad: 学習係数を各パラメータ毎にその勾配の大きさに従って減らしていく。
  ・RMSProp: 過去の更新を徐々に忘れることで、いずれ更新量が0になる点を補う。
 ・Adam: MomentumとAdaGradの融合。
Batch Normalization: NNの学習を加速させる汎用的で強力な手法。
 2015年にGoogleのLoffeさんらによって提案された。
 各層でのアクティベーションの分布を、適度な広がりを持つように調整する。
 具体的には、各ミニバッチごとにデータ分布を平均0、分散1になるように正規化。
 これにより、学習効率が上がり、初期値依存性も小さく、かつ過学習を抑制する。
・正規化: モデルの表現力が高い時、訓練データが少ない時は、過学習の対策が必要。
 ・Weight decay(荷重減衰): 大きな重みを持つことに対してペナルティを課す。
  L2正則化。複雑なニューラルネットワークには対応しきれない。
 ・Dropout: ニューロンをランダムに消去しながら学習する。
  Weight decayだけでは対応が困難なときに使用する。
ハイパーパラメータ: 学習を行う際に人が予め設定しておく必要のあるパラメータ。
 例)ニューロンの数、バッチサイズ、学習係数、Weight decay
 ・検証データ: ハイパーパラメータの調整用のデータ。
 ・ハイパーパラメータの最適化
 ⑴ハイパーパラメータの範囲を指定する。
 ⑵指定した範囲から、ランダムにサンプリングする。
 ⑶学習を行い、検証データで認識精度を評価(epochは小さく設定)。
 ⑷⑵⑶をある程度繰り返し、認識精度からハイパーパラメータの範囲を狭める。
 他にはベイズの最適化で行う方法もある。


7章 畳み込みニューラルネットワーク
全結合: 隣接する層の全てのニューロン間で結合がある。
 問題点はデータの形状が無視されてしまう点。
畳み込みニューラルネットワーク(CNN):
 これまでの全結合のネットワークに畳み込み層プーリング層が加わる。
 画像処理や音声認識など至るところで使われている。
畳み込み層(Convolutionレイヤ): データの形状を維持することができる。
 ・特徴マップ: 畳み込み層の入出力データ。
 ・畳み込み演算: 画像処理でいうところのフィルター演算
  フィルターという用語はカーネルという言葉で表現されることもある。
  フィルターのウィンドウを一定の感覚でスライドさせながら適用していく。
  それぞれで、フィルターと入力の対応する要素を乗算し、その和を求める。
  バイアス項の加算は、フィルター適用後のデータに対して行われる。
 ・パディング: 入力データの周囲に固定データ(例えば0)を埋めること。
 ・ストライド: フィルターを適用する位置の間隔。
  例)ストライド=2 → フィルターを適用する窓の間隔が2要素ごとになる。
 ・3次元の畳み込み演算→直方体のブロックで考えるとわかりやすい。
 ・バッチ処理→N回分の処理を1回にまとめて行う。
プーリング層(poolingレイヤ): 縦・横方向の空間を小さくする演算。
 学習するパラメータがない、チャンネル数不変、
 微小な位置変化に対してロバスト、といった特徴がある。
 例) 領域の最大値をとるMaxプーリング、平均値をとるavarageプーリング
im2col(image to column):
 フィルター(重み)によって都合の良いように入力データを展開する関数。
・代表的なCNN
 ・LeNet: 最初のCNN。手書き数字認識を行うネットワーク。
  シグモイド関数、サブサンプリングによる中間データの縮小を採用。
  (現在はReLU、Maxプーリングが主流)
 ・AlexNet: ディープラーニング・ブームの火付け役。
  ReLU、LRN、Dropoutを採用している点以外はLeNetとあまり変わらず。
ディープラーニングの発展に、ビッグデータGPUが大きく貢献している。    


8章 ディープラーニング
Data Augmentation: 訓練画像を回転、平行移動、切り出し(crop)、
 左右反転(flip)、輝度変化などによって、人工的に増やす手法。
・ネットワークを深くすることの利点
 ・より少ないパラメータで同レベル以上の表現力を達成できる。
 ・各層がより簡単なタスクに取り組むことになるので効率的。
ImageNet: ILSVRC(2012)で使われた100万枚を超える画像データセット
ディープラーニングフレームワーク
 ・VGG(2014):
  畳み込み層とプーリング層から構成される基本的なCNN。
  3x3の小さなフィルターによる畳み込み層を連続して行い、
  重みのある層(畳み込み層や全結合層)は16~19層。
 ・GoogLeNet(2015):
  ネットワークが横方向にも幅を持つインセプション構造
  サイズの違うフィルター(とプーリング)を複数適用し、その結果を適合。
  1x1のフィルターの畳み込み層を多くの場所で使用する事で、
  チャンネル方向にサイズを減らし、パラメータを削減。
 ・ResNet(2015):
  畳み込み層の一部をまたぐスキップ構造(ショートカット、バイパス)
  学習時のBPの勾配消失問題を軽減できる(微分が1に近くなる)。
  これにより150層以上を繋げることができるようになった。
転移学習: 学習済みの重み(の一部)を別のNNにコピーして再学習を行う。
 手元にあるデータセットが少ない場合において特に有効な手法。
GPUや分散学習、ビット精度の削減などによって学習の高速化を実現。
ディープラーニングの実用事例
 ・物体検出: 画像中から物体の位置の特定を含めてクラス分類を行う。
  R-CNN、Faster R-CNNの手法が有名。
 ・セグメンテーション: 画像のピクセルレベルでのクラス分類を行う。
  FCNという全てが畳み込み層からなるネットワークが使われる。
 ・画像キャプション設定: 入力画像の説明文を自動で生成する。
  代表的な手法はNIC(画像を理解するディープなCNNと、
  自然言語や時系列データを扱うRNNから構成)と呼ばれる。
 ・画像スタイル変換: アーティストのような画像を描かせる。
 ・画像生成: 画像をゼロから生成。DCGAN。
 ・自動運転: SegNet(CNNベース)
 ・強化学習: Deep Q-Network。最適価値行動関数。


github.com

ゼロから作るDeep Learning学習備忘録(1〜4章)

ゼロから作るDeep Learningの学習備忘録です。

github.com


目的
・最低限の外部ライブラリだけ利用し、Pythonを使って、ゼロからディープラーニングによるプログラムを実装する。
・簡単な機械学習の問題からスタートし、最終的には画像を高精度に認識するシステムを実装する。


1章 Python入門
Python: 汎用のプログラミング言語。コードがシンプルで扱いやすい。
Anaconda: データ分析などに必要なライブラリを一括してインストールできる。
NumPy: 数値計算のためのライブラリ。
Matplotlib: グラフ描写のためのライブラリ。


2章 パーセプトロン
パーセプトロン: ニューラルネットワークの起源。入出力を備えたアルゴリズム
 複数の信号を入力として受け取り、ひとつの信号を出力する。
 重みとバイアスをパラメータとして設定する。
・単層パーセプトロン→AND, NAND, OR, 線形領域
・多層パーセプトロン→XOR, 非線形領域
・多層のパーセプトロンは理論上コンピュータを実装可能。
 

3章 ニューラルネットワーク
ニューラルネットワーク: パーセプトロンの重みをデータから自動で学習できる。
 入力層、中間層(隠れ層)、出力層からなる。
 入力層で重み付けされた入力値に対して、
 中間層で重みとバイアス値を利用して多次元配列の行列計算を行い、
 活性化関数のいずれかを利用してさらに加工して結果を出力する。
活性化関数: 入力信号の総和がどのように発火するかを決定する。
 ・シグモイド関数: 入力値が大きいほど1に近づき、小さいほど0に近づく。
 ・ステップ関数: 入力値が0以下のとき0、0より大きいとき1を出力。
 ・ReLU関数: 入力値が0以下のとき0、0より大きいとき入力値をそのまま出力。
機械学習の分類
 ・分類問題: データがどのクラスに属するか。例)人の画像から性別を判断
  出力層はソフトマックス関数(1つの出力層に全ての入力が結びつく)。
 ・回帰問題: データから数値の予測。例)人の画像から体重を判断
  出力層は恒等関数(入力をそのまま出力する)。
MNIST: 手書き数字の画像セット。機械学習の分野で最も有名なデータセットの1つ。
バッチ処理: 入力データをまとめることで計算を高速化。


4章 ニューラルネットワークの学習
・データから学習する
 ・訓練データ: 学習を行い、最適なパラメータを探索する。
 ・テストデータ: 訓練したモデルの実力を評価する。
損失関数: ニューラルネットワークの学習で用いられる指標。
 損失関数を指標として、値が小さくなるように重みパラメータを更新する。
 2乗和誤差交差エントロピー誤差などがある。
・学習の流れ
 ⑴ミニバッチ学習
  膨大な量のデータに対して損失関数を計算するのは現実的ではないので、
  一部データを無作為で抽出し、近似として利用する。
  選ばれたデータをミニバッチという。
 ⑵勾配の算出
  ミニバッチの損失関数を減らすために、各重みパラメータの勾配を求める。
  勾配は、損失関数の値を最も減らす方向を示す。
 ⑶パラメータの更新
  重みパラメータを勾配方向に微小量だけ更新する。
 ⑴〜⑶を繰り返す。
・数値微分による計算は時間がかかるが簡単。
 5章の誤差逆伝播法は複雑だが高速に勾配を求めることができる。


続きはこちら
okkah.hateblo.jp