はらぺこらいおん

日々、思ったことを。

ArduinoでCO2濃度を計測する(MH-Z19)

ArduinoでCO2濃度を計測するためにいろいろやってみました。

使用パーツ

AE-ATMEGA328-MINI(Arduino Pro Mini互換)

f:id:pictzzz:20170729122213j:plain

akizukidenshi.com

秋月で780円だったので、使ってみることにしました。

USBシリアル変換

f:id:pictzzz:20170729122327j:plain

akizukidenshi.com

ロジックレベルを5Vと3.3Vで切り替えられて、便利なのでよく使ってます。

ブレッドボード

f:id:pictzzz:20170729122427j:plain

akizukidenshi.com

個人的にこれくらいのサイズが使いやすいです。サイズが足りなければ連結できます。

LCD

f:id:pictzzz:20170729122356j:plain

akizukidenshi.com

計測結果を表示するために用意しました。Arduino用のサンプルコードがあり、3.3Vと5Vで接続できます。

MH-Z19(CO2センサー)

f:id:pictzzz:20170729122259j:plain

今回のメイン、CO2センサーです。いろいろなサイトで探してみたのですが、高額だったり、大量ロットで注文しなければならなかったり、なかなか買えませんでした。

下記AliExpressという中国のサイトで見つけて安かったので、今回は試しに買ってみました。2週間ほどで無事に届きました。

AliExpress.com Product - 1PCS module MH-Z19 infrared co2 sensor for co2 monitor Free shipping new stock best quality

CO2センサーからArduinoへデータ取得

CO2センサーからの情報の取得はシリアルとPWMが選択できます。今回はPWMで取得してみようと思います。

まずは、macArduinoを接続します。ツールから以下を選択します。

  • Board: “Arduino Pro or Pro Mini”
  • Processor: “ATmega328 (5V, 16MHz)”
  • Port: “/dev/cu.usbserial-A1032FAZ”

Arduinoとセンサーの接続はシンプルで以下の3本です。

センサーが正常に動作しているかを確認するため、シリアルにログを出力するコードを実行してみます。

#define PWM_PIN 3

int prev_val = LOW;
long high_level_start = 0L;
long high_level_end = 0L;
long low_level_start = 0L;
long low_level_end = 0L;

void setup() {
  Serial.begin(9600);
  pinMode(PWM_PIN, INPUT);
}

void loop() {
  long cycle_start_time = millis();
  int pin_in = digitalRead(PWM_PIN);

  if (pin_in == HIGH) {
    if (prev_val == LOW) {
      long time_high = high_level_end - high_level_start;
      long time_low = low_level_end - low_level_start;
      long ppm = 5000 * (time_high - 0.002) / (time_high + time_low - 0.004);
      Serial.println("PPM = " + String(ppm));
      
      high_level_start = cycle_start_time;
      prev_val = HIGH;
    } else {
      high_level_end = cycle_start_time;
    }
  } else {
    if (prev_val == HIGH) {
      low_level_start = cycle_start_time;
      prev_val = LOW;
    } else {
      low_level_end = cycle_start_time;
    }
  }
}

出力は以下のようになります。室内は少しCO2濃度が高めのようです。

PPM = 905
PPM = 906
PPM = 901
PPM = 941
PPM = 940
PPM = 936
PPM = 941
PPM = 941
PPM = 920
PPM = 916
PPM = 921

仕様書を読むと2000をかけると書かれているのですが、5000にしないと値があわないようでした。

http://clima-sensor.ru/files/MH-Z19_Manual_V2.pdf

ArduinoからLCD(ディスプレイ)へ結果の出力

センサーからの結果をLCDに出力するようにしてみます。

接続は以下の4本です。

I2Cのプルアップ抵抗は初めからArduino側で実装されていましたので、そのまま接続します。

プログラムは以下のようになります。(※ 見直し中)

#include <Wire.h>

#define LCD_ADRS 0x3E
#define PWM_PIN 3

int prev_val = LOW;
long high_level_start = 0L;
long high_level_end = 0L;
long low_level_start = 0L;
long low_level_end = 0L;
int ppm = 0;

boolean is_output_ready = false;

char moji[] = "CO2 = ";

void writeData(byte t_data) {
  Wire.beginTransmission(LCD_ADRS);
  Wire.write(0x40);
  Wire.write(t_data);
  Wire.endTransmission();
  delay(1);
}

void writeCommand(byte t_command) {
  Wire.beginTransmission(LCD_ADRS);
  Wire.write(0x00);
  Wire.write(t_command);
  Wire.endTransmission();
  delay(10);
}

void init_LCD() {
  delay(100);
  writeCommand(0x38);
  delay(20);
  writeCommand(0x39);
  delay(20);
  writeCommand(0x14);
  delay(20);
  writeCommand(0x73);
  delay(20);
  writeCommand(0x52);
  delay(20);
  writeCommand(0x6C);
  delay(20);
  writeCommand(0x38);
  delay(20);
  writeCommand(0x01);
  delay(20);
  writeCommand(0x0C);
  delay(20);
}

void setup() {
  Serial.begin(9600);
  pinMode(PWM_PIN, INPUT);
  Wire.begin();
  init_LCD();
}

void loop() {
  long cycle_start_time = millis();
  int pin_in = digitalRead(PWM_PIN);

  if (pin_in == HIGH) {
    if (prev_val == LOW) {
      long time_high = high_level_end - high_level_start;
      long time_low = low_level_end - low_level_start;
      ppm = 5000 * (time_high - 0.002) / (time_high + time_low - 0.004);
      is_output_ready = true;
      
      high_level_start = cycle_start_time;
      prev_val = HIGH;
    } else {
      high_level_end = cycle_start_time;
    }
  } else {
    if (prev_val == HIGH) {
      low_level_start = cycle_start_time;
      prev_val = LOW;
    } else {
      low_level_end = cycle_start_time;
    }
  }

  if (is_output_ready) {
    writeCommand(0x00 + 0x01);
    for (int i = 0; i < 6; i++) {
      writeData(moji[i]);
    }
    String ppm_text = String(ppm);
    char ppm_print[4];
    ppm_text.toCharArray(ppm_print, 4);
    for (int i = 0 ; i < 4 ; i++) {
      writeData(ppm_text[i]);
    }
    is_output_ready = false;
  }
}

LCDへの文字の出力処理を実装し、Arduinoにアップロードします。

うまくいけば、以下のようにCO2の濃度が出力されます。

f:id:pictzzz:20170729173639j:plain

CentOS7.3にLaravel5.4をインストール

CentOS7.3にLaravel5.4をインストールした際の、記録を残しておきます。
前回の記事でセットアップした、CentOS7.3にLaravel5.4をインストールします。

pictzzz.hatenablog.com

セットアップ方法

基本的には公式ドキュメントに従って作業していきます。

laravel.com

PHPのバージョンは、PHP >= 5.6.4となっていますが、せっかくなのでPHP7をインストールしてみたいと思います。
データベースにはMariaDBを使いたいと思います。

PHPインストール

remi 追加

$ sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm

PHP7 インストール

$ sudo yum install --enablerepo=remi,remi-php70 php php-devel php-mbstring php-pdo php-xml php-zip

MariaDBインストール

$ sudo yum install mariadb mariadb-server
$ sudo yum install --enablerepo=remi,remi-php70 php-mysql

$ sudo systemctl enable mariadb.service
$ sudo systemctl start mariadb.service

$ mysql_secure_installation

Composerインストール

$ curl -sS https://getcomposer.org/installer | php
$ sudo mv composer.phar /usr/local/bin/composer

プロジェクト作成

$ composer create-project --prefer-dist laravel/laravel _project_name_

Firewall設定

$ sudo firewall-cmd --add-service=http --zone=public --permanent
$ sudo firewall-cmd --reload

開発用サーバー起動

$ cd _project_name_
$ php artisan serve --host=0.0.0.0 --port=80

ConoHaでインスタンスの初期セットアップ(CentOS7.3 64bit)

ConoHaでインスタンスの初期セットアップ(CentOS7.3 64bit)を行ったので、その記録を残しておきます。

ConoHaコントロールパネル ログイン

ConoHaコントロールパネルへのログインは公式ページより行います。

www.conoha.jp

ログインボタンからID、パスワード、2段階認証コードを入力してログインします。

インスタンス作成

ログインすると以下のような画面が表示されます。

f:id:pictzzz:20170304142344p:plain

コントロールパネルの左側のメニューからサーバーを選択します。 サーバー画面で「+サーバー」をクリックします。

f:id:pictzzz:20170304142826p:plain

接続許可ポート IPv4 / IPv6では、不要なポートはチェックを外します。 今回はリモートメンテナンス用SSHとWebサーバー用Webのみを開けています。

SSHキーは後からでも作成できます。

一般ユーザー作成

サーバー画面から、作成したサーバーを選択します。 画面上部のコンソールボタンをクリックします。

f:id:pictzzz:20170304143641p:plain

以下のようなコンソールが開きます。

f:id:pictzzz:20170304143852p:plain

このコンソールにコマンドを入力することでサーバーを操作することができます。

まずは、ユーザーを作成し、パスワードを設定します。 「username」は作成したいユーザー名になります。 パスワードは入力しても表示されません。

# adduser _username_
# passwd _username_
新しいパスワード:
新しいパスワード(再入力):

次に管理者権限でコマンドを実行できるようにします。 デフォルトでsudoの設定がwheelグループに許可されていました。 許可されるグループやユーザーを指定する場合、visudoコマンドを実行します。

# usermod -G wheel _username_

sudoコマンドが実行できるかを確認します。 viが起動すればOKです。ESCキー + :q + エンターキーで終了します。

# su - _username_
$ sudo vi a.txt
[sudo] password for _username_: 

公開鍵認証設定

リモートでサーバーへ接続するPCでキーペアを作成します。
今回はmacOSで行います。Linuxでは同じコマンドが使えるかと思います。
Windowsではputtyとかをインストールすればキーペアを生成できます。
filenameにはキーファイル名を入力します。(任意の名称)
コマンドを実行後、filenamefilename.pubが生成されているかと思います。

$ ssh-keygen -t rsa -f _filename_
$ 

filename.pubを接続するサーバーへ転送します。 今回はコンソールで入力します。

$ cat _filename_.pub
key-xxxxxxxxx

catコマンドでkeyの中身が表示されるので、コピーしておきます。

Webのコンソールを開きます。rootになっている場合、作成したユーザーに切り替えます。 今ログインしているユーザーはidコマンドで調べることができます。

# su - _username_

ホームディレクトリー下に.sshディレクトリーを作成します。ディレクトリーのパーミッションを700に変更します。 作成した.sshディレクトリー下にauthorized_keysを作成します。authorized_keysには、先ほどコピーしたキーを貼り付けます。
authorized_keysはパーミッションを600に変更します。

$ mkdir .ssh
$ chmod 700 .ssh
$ vi .ssh/authorized_keys
$ chmod 600 .ssh

sshd設定

sshdの設定を変更して、rootでログインできないようにしておきます。

$ sudo vi /etc/ssh/sshd_config
#Port 22
↓
Port 22

#Protocol 2
↓
Protocol 2

#RSAAuthentication yes
#PubkeyAuthentication yes
↓
RSAAuthentication yes
PubkeyAuthentication yes

#Banner none
↓
Banner none

PermitRootLogin yes
↓
PermitRootLogin no

変更したらsshdを再起動します。

$ sudo systemctl restart sshd

PCから接続

今回はmacOSからの接続の場合、ターミナルを開いて、sshコマンドを実行します。 ip_addressにはサーバーのIPアドレスを指定します。ConoHaのコントロールパネルに表示されています。 key_pathには今回生成したキーペアの.pubが付いていない方を指定します。

初回接続時には警告が出ますが、yesで進めます。

$ ssh _username_@_ip_address_ -i _key_path_

以上で、セットアップは完了です。

AWSのEBS(ストレージ)を追加する方法

スポットインスタンス機械学習を行っていると、 学習を行っている間はインスタンスを起動しておき、学習を完了した際には、 インスタンスを終了するということを行います。

そうすると学習データや学習したモデルを毎回学習を行うたびにアップロードする必要があります。 AWSではインバウンドトラフィックAWSへの通信)にコストはかかりませんが、 大量の画像データをアップロードするには時間がかかってしまい、 時間とインスタンス料金がもったいないので、 先にデータを保存しておくEBSを別に作っておきます。

そしてインスタンスを起動したら、そのEBSを接続することで、 データを再アップロードせずに済むようにしておきます。

EBS作成

EBSでディスクを作成するのは簡単です。

メニューのボリュームをクリックします。 EBSの画面に表示される、ボリュームの作成をクリックします。

f:id:pictzzz:20170209230628p:plain

わかりやすい名前をつけて、必要なディスクサイズを入力します。

f:id:pictzzz:20170209230727p:plain

接続したいディスクを選択し、アクション>ボリュームのアタッチをクリックします。

f:id:pictzzz:20170209230810p:plain

ボリュームのアタッチを行うウィンドウが表示されます。 アタッチするインスタンスインスタンスにアタッチした際のデバイスを入力します。 フォームをクリックすると、選択肢が表示されるので、そこから選ぶことができます。 入力後、アタッチをクリックするとインスタンスにボリュームが接続されます。

f:id:pictzzz:20170209230853p:plain

マウント

インスタンスに接続します。 マウントするためにはrootになる必要があります。

$ sudo su -

まずは、デバイス名を調べます。デバイス名を調べるのにはlsblkコマンドを使います。

# lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0  20G  0 disk 
└─xvda1 202:1    0  20G  0 part /
xvdf    202:80   0  30G  0 disk 

マウントされてないのはxvdfとわかったので、/dev/xvdfをマウントします。 マウントポイントは任意ですが、今回はルートディレクトリー直下にdataディレクトリーを作って、マウントしたいと思います。

# mkdir /data
# mount /dev/xvdf /data

これで/dataに作成したEBSボリュームが接続され、使用できるようになります。

初めてのマウントの場合(フォーマット)

初めてのマウントの場合、マウントの前にフォーマットを行います。

# mkfs -t ext4 /dev/xvdf

これでext4でフォーマットされます。

AWS CUDA8.0セットアップ済みAMI作成

pictzzz.hatenablog.com

前回の記事ではAWSのP2インスタンスをスポットで利用する方法をまとめました。 これで安くEC2を使うことができるようになりましたが、毎回毎回OSからCUDA、プログラミング環境をセットアップするのは大変手間です。

そこでAMIという仕組みを使い、この問題を解決したいと思います。

AMIとは

AMIとは、スナップショットとインスタンスの管理情報が入っており、 これを使ってOS、CUDA、プログラミング環境のセットアップ済みのインスタンスを直接起動できるようになります。

docs.aws.amazon.com

AMI作成

AMIの作成は簡単です。AMIを作成したいインスタンスを選択し、「アクション」>「イメージ」>「イメージの作成」の順にクリックします。

f:id:pictzzz:20170208223141p:plain

イメージの作成をクリックするとダイアログが表示されるので、名称をつけて保存します。

f:id:pictzzz:20170209222343p:plain

ここでの注意点は以下のものがあります。

  • 再起動しないにチェックを入れないと、インスタンスが再起動されてしまいます。
  • 再起動をしないでも通常は正常にAMIを作ることができます。
  • スポットインスタンスを使っている場合でも、再起動は可能です。
  • AWSではデータの整合性のため、再起動してAMIを作成することを推奨しています。

AMIからスポットインスタンス作成

作成したAMIからスポットインスタンスを作成して見たいと思います。

通常のスポットインスタンスのリクエストページへ移動します。 (EC2 -> スポットインスタンス -> スポットインスタンスのリクエスト)

画面の中ほどにAMIを選択する部分があるのでクリックします。

f:id:pictzzz:20170208224728p:plain

カスタムAMIを使うという選択肢が表示されるので選択します。

f:id:pictzzz:20170208224820p:plain

すると作成したAMIが表示されるので、使用するAMIを選びます。

f:id:pictzzz:20170209221002p:plain

これの状態で通常通りスポットインスタンスのリクエストを行うと、AMIが使用されてインスタンスが作成されます。

f:id:pictzzz:20170208225419p:plain

このようにして作成したインスタンスは、AMI作成時のインスタンスの状態が復元されます。
ただし、グローバルIP(EIP)は付け直す必要があります。

以上で、AMIを使ったスポットインスタンスの作成方法の説明は終了となります。

AWSのEC2 P2スポットインスタンスにCUDA8.0とChainerをセットアップ

AWSでP2インスタンスがリリースされました!(結構前になりますが。。。)

Amazon EC2 P2 インスタンス| AWS

今までのG2インスタンスやElasticGPU(インスタンスにアタッチできるGPU)より、汎用的な計算に向いており、Tesla K80を搭載しているようです。

今回はp2.xlargeのスポットインスタンスを使ってみたいと思います。簡単に行いたい場合はオンデマンドインスタンスの方が良いです。オンデマンドインスタンスの場合、NVIDIA公式からドライバー等がセットアップされたAMI(OSのテンプレートみたいなもの)が提供されているためです。

それでもなぜスポットインスタンスを使うのかというと、価格が安いからです。以下に料金がありますが、P2インスタンスの中で一番小さいp2.xlargeで$0.9/1 時間(約100円/1時間)となります。バージニア北部リージョンの場合。ちなみに東京リージョンにはP2インスタンスはまだありません。(2017/1/31現在)

EC2 インスタンスの料金 – アマゾン ウェブ サービス (AWS)

対してスポットインスタンスはだいたい$0.1〜0.2/1時間(約10〜25円/1時間)となってます。たまに高くなっている時もありますが、安い時に使って、終了したら消してしまう機械学習には丁度いい感じがします。

f:id:pictzzz:20170131222806p:plain

今回はUbuntu14.04をOSとして選択しました。なので、以前ブログに載せた記事とほぼ同じ手順になります。

pictzzz.hatenablog.com

AWSセットアップ

P2インスタンスを使うためにはVPCを用意しておく必要があります。スポットインスタンス作成を行う道すがら必要な環境のセットアップ用リンクが用意されているのでそれを使って設定していきます。

まずAWSにログインすると以下のような画面が出るかと思います。

f:id:pictzzz:20170131225215p:plain

まずは右上のリージョンを確認します。P2インスタンスは一部リージョンにしかないため、P2インスタンスの起動できるリージョンを選択します。今回はバージニア北部を選択しました。

次にEC2のメニューへと進みます。画面のEC2か、左上のサービスからEC2を選択します。

f:id:pictzzz:20170131232004p:plain

EC2の画面の左側のメニューからスポットリクエストを選択します。

f:id:pictzzz:20170131232057p:plain

青いボタンのスポットインスタンスのリクエストを選択します。

f:id:pictzzz:20170131232215p:plain

そうするとスポットインスタンスの作成ウィザードが起動します。 ここで今回はAMIをUbuntu14.04に変更し、インスタンスタイプをp2.xlargeを選択します。

f:id:pictzzz:20170131232346p:plain

インスタンスタイプを選択する際に表示されるウィンドウの右上の価格設定履歴から過去のスポットインスタンスの価格が見れます。

f:id:pictzzz:20170131232444p:plain

ネットワークの選択でP2インスタンスは最初からあるEC2-Classicが選択できません。そのため、VPCを新しく作る必要があります。 ネットワークの右側にある新しいVPCの作成をクリックします。

f:id:pictzzz:20170131232833p:plain

クリックするとVPCの作成画面に移動します。VPCの作成をクリックします。

f:id:pictzzz:20170131232903p:plain

VPCは以下のように作りました。インスタンスを1つだけ作る場合、特にどのような設定でもOKだと思います。今回は10.0.0.0/24のネットワークにIPv6はなしとしました。

f:id:pictzzz:20170131233055p:plain

次にサブネットを作成します。VPCの画面の左側にサブネットのメニューがあるのでクリックします。

f:id:pictzzz:20170131233346p:plain

サブネットは以下のように作りました。先ほど作成したVPCに紐付けて10.0.0.0/28のネットワークに、アベイラビリティゾーン(AZ)はaとしました。どのAZを選択しても問題ありません。ただし、AZごとに需要に応じてスポットインスタンスの価格が違うので注意です。

f:id:pictzzz:20170131233440p:plain

AWSからインターネットに抜けるためにインターネットゲートウェイを作成します。サブネットの画面の左側にインターネットゲートウェイのメニューがあるのでクリックします。インターネットゲートウェイの作成をクリックします。

f:id:pictzzz:20170131234049p:plain

名前を適当に入力します。

f:id:pictzzz:20170131234120p:plain

作ったインターネットゲートウェイVPCを紐付けします。VPCにアタッチをクリックします。

f:id:pictzzz:20170131234156p:plain

紐付けるVPCを選択します。

f:id:pictzzz:20170131234210p:plain

状態がattachedになればOKです。

f:id:pictzzz:20170131234246p:plain

さらにルートテーブルも作っておきます。インターネットゲートウェイの画面の左側にルートテーブルのメニューがあるのでクリックします。先ほど作成したVPCに紐付いたルートテーブルがあるためこれにインターネット向きのルートを追加します。

f:id:pictzzz:20170131233713p:plain

ルートテーブルを選択し、下に表示される詳細のルートを選択します。その画面に表示される編集をクリックします。

f:id:pictzzz:20170131233828p:plain

送信先:0.0.0.0/0、ターゲット:先ほど作成したインターネットゲートウェイとして保存します。

f:id:pictzzz:20170131234332p:plain

ここまででEC2インスタンスを作成する環境の構築が完了です。スポットインスタンスの作成ウィザードへ戻ります。(別のウィンドウかタブで開いているはずです。

作成したVPCとサブネットを選択します。最後の最高価格はスポットインスタンスの価格が上昇しても払えると思える最大の金額になります。この金額はあくまで払おうと思っている最大値であって、この金額が請求されるわけではありません。が、スポットインスタンスの価格がこの価格を超えると、2分後にインスタンスが強制終了されます。

f:id:pictzzz:20170131234650p:plain

インスタンスにつけるボリュームを作成します。NVIDIAのドライバーやCUDAのインストールファイルが大きいので15GB以上はあった方がいいと思います。デフォルトの8GBだと確実に足りません。画像や動画の学習を行う場合、もっと容量はいると思います。もちろん、別途EBS(ディスク)を接続することもできます。

f:id:pictzzz:20170131234939p:plain

画面下部ではキーペアを選択します。ここで作成したキーを使ってインスタンスSSH接続します。キーペアを新しく作る場合は、右側の新しいキーペアの作成をクリックします。

f:id:pictzzz:20170131235854p:plain

キーペアの作成をクリックします。

f:id:pictzzz:20170201001733p:plain

キーペアの名前を適当に入力します。

f:id:pictzzz:20170201001800p:plain

作成が完了するとダウンロードされます。

f:id:pictzzz:20170201001910p:plain

スポットインスタンスの作成ウィザードへ戻ります。
一番下まで行き、確認をクリックします。その後、確認画面が出ますので、作成をクリックすると、スポットインスタンスのリクエストが作成されます。入力した金額をスポットインスタンスの価格が下回った場合、実際にインスタンスが作成されます。

画面左側のメニューからインスタンスの画面に移動します。作成されたスポットインスタンスはこの画面に表示されます。ステータスが2/2になったらアクセス可能です。

f:id:pictzzz:20170201000158p:plain

起動するまでに数分かかりますので、その間にEIP(グローバルIP)とセキュリティグループ(FWのようなもの)を作成しておきます。左側のメニューからElastic IPをクリックします。新しいアドレスの割り当てをクリックします。

f:id:pictzzz:20170201000344p:plain

スコープはVPCを選択します。

f:id:pictzzz:20170201000441p:plain

グローバルIPが割り当てられます。アクションのアドレスの関連付けをクリックします。

f:id:pictzzz:20170201000724p:plain

先ほど作成したインスタンスに紐付けます。

f:id:pictzzz:20170201001105p:plain

画面左側のメニューからセキュリティグループを選択します。作成したVPCに紐付いているセキュリティグループを選びます。下に詳細が出てくるので、インバウンドをクリックします。編集をクリックします。

f:id:pictzzz:20170201001223p:plain

SSH(22ポート)を送信元0.0.0.0(全て)で許可します。送信元が特定される場合は、ここでIPアドレスを入力して制限することをお勧めします。

f:id:pictzzz:20170201001403p:plain

ダウンロードしたキーを使ってSSH接続します。キーはホームディレクトリーの下のkeysディレクトリーに置いてあることを想定してます。

$ cd ~/keys
$ chmod 600 dnn-key.pem
$ ssh -i dnn-key.pem ubuntu@xxx.xxx.xxx.xxx

CUDAセットアップ

ここから作成したインスタンスにCUDAをセットアップします。
以下のURLからCUDAのセットアップファイルをダウンロードし、インストールします。

CUDA 8.0 Downloads | NVIDIA Developer

f:id:pictzzz:20170201002718p:plain

このファイルをダウンロードしました。

f:id:pictzzz:20170201003321p:plain

コマンドは最新のダウンロードURLを確かめてください。

wget https://developer.nvidia.com/compute/cuda/8.0/prod/local_installers/cuda_8.0.44_linux-run
mv cuda_8.0.44_linux-run cuda_8.0.44_linux.run
chmod +x cuda_8.0.44_linux.run

OSのアップデートとnouveau無効化を行います。

  • OSアップデート
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install build-essential git python-pip python-dev libxml2-dev libxslt1-dev zlib1g-dev libcurl4-openssl-dev libatlas-base-dev linux-image-extra-virtual libopencv-dev python-numpy
sudo apt-get autoremove
  • nouveau無効化
# sudo vi /etc/modprobe.d/blacklist-nouveau.conf

blacklist nouveau
blacklist lbm-nouveau
options nouveau modeset=0
alias nouveau off
alias lbm-nouveau off
# sudo vi /etc/modprobe.d/nouveau-kms.conf

options nouveau modeset=0
$ sudo update-initramfs -u
# sudo vi /etc/default/grub

# GRUB_CMDLINE_LINUX_DEFAULT=""  # ""の部分にtextを追加
GRUB_CMDLINE_LINUX_DEFAULT="text"
sudo reboot
$ sudo apt-get install linux-headers-`uname -r`

インストールしておかないと以下のようなエラーが出て、CUDAのインストールに失敗したのでインストールしておきます。

The driver installation is unable to locate the kernel source. Please make sure that the kernel source packages are installed and set up correctly.
If you know that the kernel source packages are installed and set up correctly, you may pass the location of the kernel source with the '--kernel-source-path' flag.

デフォルトでインストールします。ドライバーもインストールします。

./cuda_8.0.44_linux.run 

Do you accept the previously read EULA?
accept/decline/quit: accept

Install NVIDIA Accelerated Graphics Driver for Linux-x86_64 367.48?
(y)es/(n)o/(q)uit: y

Do you want to install the OpenGL libraries?
(y)es/(n)o/(q)uit [ default is yes ]: 

Do you want to run nvidia-xconfig?
This will update the system X configuration file so that the NVIDIA X driver
is used. The pre-existing X configuration file will be backed up.
This option should not be used on systems that require a custom
X configuration, such as systems with multiple GPU vendors.
(y)es/(n)o/(q)uit [ default is no ]: 

Install the CUDA 8.0 Toolkit?
(y)es/(n)o/(q)uit: y

Enter Toolkit Location
 [ default is /usr/local/cuda-8.0 ]: 

Do you want to install a symbolic link at /usr/local/cuda?
(y)es/(n)o/(q)uit: y

Install the CUDA 8.0 Samples?
(y)es/(n)o/(q)uit: y

Enter CUDA Samples Location
 [ default is /home/ubuntu ]: 

以下のパスを環境変数に追加しておきます。

# vi .bashrc

export CUDA_PATH=/usr/local/cuda-8.0
export LD_LIBRARY_PATH=/usr/local/cuda-8.0/lib64
export PATH=$PATH:$CUDA_PATH/bin

export CFLAGS=-I$CUDA_PATH/include
export LDFLAGS=-L$CUDA_PATH/lib64

環境変数を追加後、以下のコマンドでGPUの情報が取得できるようになります。

$ source .bashrc
$ nvidia-smi
Tue Jan 31 13:10:52 2017       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 367.48                 Driver Version: 367.48                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla K80           Off  | 0000:00:1E.0     Off |                    0 |
| N/A   34C    P0    71W / 149W |      0MiB / 11439MiB |     99%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

f:id:pictzzz:20170201003418p:plain

こちらのURLから登録を行ってダウンロードしました。

NVIDIA cuDNN | NVIDIA Developer

ダウンロードしたファイルは以下のcuDNN v5.1 Library for Linuxです。ファイルはダウンロード後、scpでインスタンスにアップロードします。

f:id:pictzzz:20170201003547p:plain

ライブラリーディレクトリーにコピーします。

$ tar xfz cudnn-8.0-linux-x64-v5.1.tgz 
$ sudo cp cuda/include/cudnn.h $CUDA_PATH/include/
$ sudo cp cuda/lib64/* $CUDA_PATH/lib64/

Chainerセットアップ

最後にChainerをインストールします。Chainerはpipでインストールできます。今回はユーザーランドにインストールします。

pip install --user chainer -vvvv
$ pip freeze | grep chainer
chainer==1.20.0.1

最新バージョンがインストールされていることが確認できました!

セットアップは以上となります。お疲れさまでした!

Let’s enjoy deep learning life on AWS!

Kerasで2種類(クラス)への分類

前回と前々回にセットアップしたKerasを使って2種類の分類を行ってみたいと思います。

前回と前々回のセットアップ記事はこちらからどうぞ。

pictzzz.hatenablog.com

pictzzz.hatenablog.com

概要

入力に対して2種類の分類を行う用途は意外と多く、有用だと思います。 例えば、性別、年齢、居住地、母国語、といった属性データに対して、 ある製品を購入した、していないの分類や、 テレマーケティングの結果、購入してくれるか、してくれないか、明日の株価は上がるのか下がるのかといったことなどなど、 様々な用途に使えると思います。

使用データ

今回は仮想的に以下のような構造を持ったcsvから学習を行うことを考えてみます。 このデータはテレマーケティングを行ってみて、購入したかどうかの結果がデータに反映されています。

#train.csv

ID,名前,性別,居住地,年齢,預金残高,契約年数,累積通話時間,購入したか
1,太郎,男,東京,20,100000,1,3,0
2,花子,女,大阪,40,1000000,10,10,1
・
・
・

このようなデータが1万レコードほどあると想定してください。
最後の購入したかは0が購入しなかった、1が購入したとなります。

対して、テストしてみるファイルには購入したかが入っていません。 イメージとしてはこれからどの人に優先的にアプローチをかけていけば良いかを 判断したいと考えてください。

#test.csv

ID,名前,性別,居住地,年齢,預金残高,契約年数,累積通話時間
1,一郎,男,北海道,51,11300000,4,5
2,さくら,女,沖縄,43,131000000,14,12
・
・
・

使用するモデル

多層パーセプトロンMLP)とします。Kerasではplotを利用することでモデルを画像として保存することができます。

from keras.utils.visualize_util import plot

plot(model, to_file='model.png')

実際に保存されたモデルがこちらです。

f:id:pictzzz:20170126141022p:plain

Denseと書かれているレイヤーは全結合レイヤー(fully connected layer)となります。

各レイヤーのユニット数は以下のようにしてます。
53(input) -> 500 -> 1000 -> 50 -> 2(out)

レイヤーの数やユニット数はいろいろ変えてみて、試してください。

入力、出力のユニット数

出力

今回は購入した(True)か購入しなかった(False)かの判定ですので、出力のユニット数は2となります。 出力ユニットが[1, 0]の場合、購入した、[0, 1]の場合、購入しなかったと判断します。

入力

入力は入力ユニットにデータを1つ1つ入れていきます。 ユニット数が53になっているのは、one hotエンコーディングでデータを入力するためです。

例えば、性別は、データの中に男性、女性の2種類がありますが、このままの状態ですと ネットワークで扱いにくいため、one hotエンコーディングというものを行います。

One-hot - Wikipedia

これを適用すると以下のようなデータに変換できます。

男性 = 10
女性 = 01

同じように年月の月のデータがある場合は、以下のように変換できます。

"jan" =  [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
"feb" =  [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
"mar" =  [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
"apr" =  [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
"may" =  [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
"jun" =  [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
"jul" =  [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
"aug" =  [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
"sep" =  [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
"oct" =  [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
"nov" =  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
"dec" =  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]

Kerasには自動でone hotエンコーディングを行ってくれる機能があります。 to_categoricalでone hotではないデータと分類するクラスの数(例えば月の場合は12)を指定すると、 one hotなデータを得られます。

from keras.utils import np_utils

nb_class = 12
one_hot_dataset = np_utils.to_categorical(not_one_hot_dataset, nb_classes)

その他テクニック

その他のテクニックとしては、正規化と中間値補完があります。

正規化では例えば預金残高が大きな数字になっていますのでこれを0〜1の間に収まるように、 入力データ全体の中で最大の預金残高で全ての預金残高の値を割ります。

中間値補完では例えば年齢の入力データの中に年齢が入っていない人がいる場合、 データを代表するような値(例えば、中間値)で代替することを行います。

データの前処理で用意するデータ

以上のような処理を行い、以下の5つのデータを準備します。

  • 学習用入力データ
  • 学習用教師データ
  • テスト用入力データ
  • テスト用教師データ
  • 新しい入力データ

学習用入力データと教師データ、テスト用入力データと教師データは対になっている必要があります。
学習用とテスト用のデータは同じ形式のものですが、学習で使ったデータと同じものでネットワークを評価してしまうと、 未知のデータに対する精度がわからないため、学習用データから一部を学習に使わずにテストに使う用として、 予め別に保持しておきます。 Kerasの場合は、テスト用データは別に準備しなくても学習用データの 一部を自動でテスト用データとして扱うことができます。 新しい入力データは、学習に使わず、答えが未知のデータとなります。このデータに対して、予測を行います。

モデルの作成

モデルは上に記載したように、いくつかレイヤーを重ねたものとしています。 Kerasでは簡単にモデルを変更することができます。

今回のモデルは以下のように記述します。

from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation

model = Sequential()

model.add(Dense(500, input_shape=(53,)))
model.add(Activation('sigmoid'))
model.add(Dropout(0.2))

model.add(Dense(1000))
model.add(Activation('sigmoid'))
model.add(Dropout(0.2))

model.add(Dense(50))
model.add(Activation('sigmoid'))
model.add(Dropout(0.2))

model.add(Dense(2))
model.add(Activation('softmax'))

活性化関数にはsigmoidとsoftmaxを使っています。 ドロップアウトには0.2を指定しています。ドロップアウトは指定された確率で各ユニットが無効になり、 ネットワークは無効になった以外のユニットだけで予測をする必要が出てきます。 これにより過学習という特定のデータに対して強く適合したネットワークになることを緩和できると言われています。

モデルを定義したらコンパイルを行います。今回は2つのユニットに出力を行うので損失関数にcategorical_crossentropyを指定します。 もし、1つのユニットに出力を行う場合は、binary_crossentropyを指定します。

最適化アルゴリズムにはadamを指定しました。 その他のアルゴリズムもKerasには定義されています。 詳しくは下記の公式URLを参照してみてください。 最適化 - Keras Documentation

model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

学習

学習にあたり、過学習を防ぐ意味でも、アーリーストップを設定することができます。 アーリーストップは値が収束した時点で学習をストップするものになります。

from keras.callbacks import EarlyStopping

early_stopping = EarlyStopping(monitor='val_loss', patience=2)

Kerasの学習は以下のようにして行います。

hist = model.fit(x_train, y_train,
                 batch_size=batch_size,
                 verbose=1,
                 nb_epoch=nb_epoch,
                 validation_split=0.1,
                 callbacks=[early_stopping])

指定しているパラメーターの意味は以下のようになります。

  • x_train:学習データ
  • y_train:学習データの教師データ
  • batch_size:バッチサイズ(*)
  • verbose:ログ出力をプログレスバーで行う(0:出力なし、2:ログ出力)
  • nb_epoch:エポック数(全ての学習データに対するイテレーション数)
  • validation_split:学習データの中で検証に使うデータの割合
  • callbacks:学習中に行う処理のリスト(今回はアーリーストップを指定)

(*)バッチサイズ:複数のデータの誤差をまとめて、1回で更新を行う、ミニバッチ法の設定値になります。

評価

学習した結果の評価にはevaluateを使用します。

score = model.evaluate(x_test, y_test)

print('Test loss:', score[0])
print('Test accuracy:', score[1])

このように指定することでモデルの精度と誤差関数の値を出力することができます。

モデルの保存

学習済みのモデルの保存は以下のようにsaveを使います。

model.save('mlp_model.h5')

学習済みのモデルをロードする際には、load_modelを使います。

from keras.models import load_model

model = load_model('mlp_model.h5')
model.summary()

学習データの活用

学習したモデルに対して予測を行うにはpredictを使います。

y = model.predict(x)

print(y)

predictにはnumpy arrayで複数のデータを渡して、一度に予測することができます。

今回のモデルではpredictの出力は2つあり、[1, 0]が購入した(True)を示しているので、 y[i][0]がi番目の人が購入する確率を示すことになります。

i,y[0]
1,0.6803011298
2,0.6074721813
3,0.0003010118
4,0.0002940704
5,0.0291656982
6,0.0007526643
7,0.0005948166
8,0.0001619863

真理値で知りたい場合はroundで四捨五入を行うことで0 or 1の結果を得ることができます。