Hivemall Getting Started
この記事はHadoop/Hive/Hivemall環境をローカルマシン(mac)上に構築して、テストしてみたい方向けに執筆しています。
目次
はじめに
昨今、数多くの機械学習向けライブラリがリリースされています。今回紹介するHivemallは、HiveUDF上で動作するため、大量データに対して分散処理を行う際に高い性能を示します。分散環境の構築を1から始めるのは容易ではないですが、擬似分散モードであれば、マシンが1台あれば比較的容易に構築から動作確認まですることができます。今回は、 Hivemallを使って、Kaggleのチュートリアル的タイトルの「Titanic」に挑戦したいと思います。 今回やることは以下の通りです。- HomeBrewのインストール
- Hadoopのインストール
- Hiveのインストール
- Hivemallのインストール
- Hivemallによる機械学習
Javaのインストール
今回、インストールするソフトウェアを動かすには、Java7以上が必要になります。お使いのマシンに、インストールされていないようであれば、Oracleから最新バージョン(Java8)をダウンロードしてください。Homebrewのインストール
今回は、できるだけ簡単に進めるために、Homebrewを使用します。Homebrewがインストールされていない場合はインストールしてください。下記コマンドでインストールできます。$ /usr/bin/ruby -e
"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
以降、特に断りがなければ、「$...
」はターミナルからのコマンド実行を示しています。
Homebrewについて詳しくは、下記URLを参照してください。
Hadoopのインストール
ここでは、HadoopをSingle Node Clusterで利用できるようにします。Homebrewがインストールされていれば、なんら恐れる必要はありません。次のコマンドひとつでインストールできます。$ brew install Hadoop
インストールが完了したら、正常にHadoopがインストールされているかを確認してみましょう。ここではスタンドアロンモードで動作を確認します。これには、Hadoopに同封されているテストプログラムを利用した単語の集計(wordcount)を利用します。
では、テスト用の入力データを作成しましょう。
$ echo "dog dog dog cat cat cat" > input/test1.txt
$ echo "dog dog dog" > input/test2.txt
Hadoopを起動します。
$ HADOOP_INSTALL=/usr/local/Cellar/hadoop/2.8.0/libexec
$ hadoop jar ${HADOOP_INSTALL}/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.8.0.jar wordcount input output
ここでは、inputディレクトリ以下のファイルが読み込まれて、ファイル内に記載される単語の件数が集計され、outputディレクトリに出力されます。${HADOOP_INSTALL}は、インストール環境によって異なる値となるため、設定前に確認してください。Homebrewのデフォルト設定であれば、「/usr/local/Cellar」以下にインストールされていると思います。集計している間は、色々とログが出ます。
結果を確認します。
$ ls output/
_SUCCESS part-r-00000
集計された結果が「part-r-xxxx」の形式で出力されます。_SUCCESSは、正常終了された場合に出力される空のファイルです。
$ cat output/*
dog 6
cat 3
ちゃんと集計されていることがわかります。
次にHiveを利用可能な状態にするためにHadoopを擬似分散モードで起動できるようにします。擬似分散モード実行には、マシンへのssh接続許可が必要です。「システム環境設定」から「共有」を選択し、「リモートログイン:入」に設定してください。
また、擬似分散モードにするためには設定ファイルを変更する必要があります。Hadoopにはたくさんの設定ファイルがありますが、テスト的に動作させる分には、次のような変更で十分です。
$ vi ${HADOOP_INSTALL}/etc/hadoop/core-site.xml
<configuration>
</configuration>
⬇
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://localhost:9000</value>
</property>
</configuration>
$ vi ${HADOOP_INSTALL}/etc/hadoop/hdfs-site.xml
<configuration>
</configuration>
⬇
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
</configuration>
Namenodeを初期化します。
$ hdfs namenode –format
*localhostのパスワードを要求される場合、ログインパスワードを入力してください。
Hadoopを分散モード(HDFS)で起動します。
$ ${HADOOP_INSTALL}/sbin/start-dfs.sh
JavaのjpsツールでHDFSの起動を確認します。
$ jps
14561 NameNode
14754 SecondaryNameNode
14646 DataNode
数値は、その時によって変わります。この3つのプロセスが動いていることを確認してください。
また、以下のURLに接続できることを確認してください。
- http://localhost:50070/
$ hdfs dfs -mkdir /test
$ hdfs dfs -ls /
Found 1 items
drwxr-xr-x - user group 0 2017-00-00 00:00 /test
HomeBrewでインストールした場合、以下のようなWARNログが出る場合があります。
WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform..今回の確認では、動作に支障ありませんので、そのまま勧めていただいても構いません。解決方法はAPPENDIXを参照してください。 先に作成したテストデータをHDFS管理下にコピーします。
$ hdfs dfs -mkdir /test/hdfs_input
$ hdfs dfs -put input/* /test/hdfs_input
擬似分散環境下でのwordcountの動作を確認しましょう。
$ hadoop jar ${HADOOP_INSTALL}/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.8.0.jar wordcount /test/hdfs_input /test/hdfs_output
$ hdfs dfs -cat /test/hdfs_output/*
dog 6
cat 3
問題なく動作していることが確認できたので、Hiveのインストールに続きたいと思います。
Hiveのインストール
ここでは、Hiveをインストールし接続できるようになるところまでを確認します。HiveもHomebrewを利用すれば、簡単にインストールできます。$ brew install hive
これでインストールは完了したので、初期設定と接続確認を行っていきましょう。
Hiveメタストアを初期化します。
$ schematool -dbType "derby" –initSchema
HiveServerを起動します。
$ hiveserver2 &> hiveserver2.log &
クライアントを起動し、Hiveに接続します。
$ beeline -u jdbc:hive2:// --color=true
schematool実行時にmetastore_dbが作成されます。クライアント起動時にmetastore_dbを参照しています。beelineコマンドはmetastore_dbディレクトリの配置されるディレクトリと同じディレクトリ内で実行してください。
テストとして、データベースの一覧を表示します。
//> show databases;
+----------------+--+
| database_name |
+----------------+--+
| default |
+----------------+--+
*以降、特に断りがなければ、「//>」はbeelineプロンプトからのクエリ実行になります。
初期化以外何もしていないので、default以外の表示はないと思います。
Hivemallのインストール
HivemallはHiveUDFなためHomebrewではインストールできません。HivemallのWEBページから、「1.2. Getting Started」「1.2.1. Instllation」「Prerequisites」下の「hivemall-core-xxx-with-dependencies.jar」リンク先からjarファイルと「define-all.hive」をダウンロードしてください。 beelineから、以下のコマンドを実行してください。//>
add jar ${Download}/hivemall-core-0.4.2-rc.1-with-dependencies.jar;
//>
source ${Download}/define-all.hive;
sourceは正常終了しない場合があります。その場合はdefine-all.hive内の「CREATE FUNCTION」を手動実行することで、代替する必要があります。
Hivemallによる機械学習
ようやく、本題になります。今回は、KaggleのTitanicデータに対してRandomForestを行い、生存者予測を行いたいと思います。データの取得
まずは、Kaggleに登録して、データをダウンロードしてください。検索フォームから「titanic」と検索し、「Titanic: Machine Learning from Disaster」(https://www.kaggle.com/c/titanic)を開けば、「Data」から訓練データtrain.csvとテストデータtest.csvをダウンロードできます。 ここからは、Hivemallの「Kaggle Titanic tutorial」ページの内容に沿って進めていきたいと思います。データ準備
まずは、データベースを作成します。//> create database titanic;
作業用にCURRENT_DBを切り替えます。
//> use titanic;
訓練データ用のTABLEを作成します。
//>
create external table train (
passengerid int, -- unique id
survived int, -- target label
pclass int,
name string,
sex string,
age int,
sibsp int, -- Number of Siblings/Spouses Aboard
parch int, -- Number of Parents/Children Aboard
ticket string,
fare double,
cabin string,
embarked string
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '|'
LINES TERMINATED BY '\n'
STORED AS TEXTFILE LOCATION '/dataset/titanic/train';
データは次の工程で追加します。そのまま先に進んでください。
テストデータ用のTABLEを作成します。
//>
create external table test_raw (
passengerid int,
pclass int,
name string,
sex string,
age int,
sibsp int, -- Number of Siblings/Spouses Aboard
parch int, -- Number of Parents/Children Aboard
ticket string,
fare double,
cabin string,
embarked string
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '|'
LINES TERMINATED BY '\n'
STORED AS TEXTFILE LOCATION '/dataset/titanic/test_raw';
データロード
先ほど作成したテーブルにデータをロードします。$ sed -e "1d" -e "s/, /. /g" -e"s/\"//g" -e "s/,/|/g" train.csv \
| hdfs dfs -put - /dataset/titanic/train/train.csv
FPATを利用したawkは、意図した動作をしない場合があるので、sedで代替しています。
同様にテストデータもLOCATIONにputします。
これで、Hiveからデータが確認できるようになっているはずです。確認してみます。
//>
select passengerid,pclass,name,sex,age,sibsp,parch,ticket,fare,cabin,embarked from train limit 1;
+--------------+---------+--------------------------+-------+------+--------+--------+------------+-------+--------+-----------+--+
| passengerid | pclass | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked |
+--------------+---------+--------------------------+-------+------+--------+--------+------------+-------+--------+-----------+--+
| 1 | 3 | Braund. Mr. Owen Harris | male | 22 | 1 | 0 | A/5 21171 | 7.25 | | S |
+--------------+---------+--------------------------+-------+------+--------+--------+------------+-------+--------+-----------+--+
前処理
データ前処理の前にhiveの設定を一時的に変更します。//> set hive.cbo.enable=false
次に使うUDTF:quantifyは現在、CBO管理外となっているため、一時的に使用不可とします。trueの場合、ERRORログが出ます。ログ出力されても、動作上は問題ないですが気持ちが悪いので停止します。
前処理UDTF用の変数を設定します。
//> set hivevar:output_row=true;
設定ができたので、データの前処理を行なっていきます。
//>
create table train_rf
as
WITH train_quantified as (
select
quantify(
${output_row}, passengerid, survived, pclass, name, sex, age, sibsp, parch, ticket, fare, cabin, embarked
) as (passengerid, survived, pclass, name, sex, age, sibsp, parch, ticket, fare, cabin, embarked)
from (
select * from train
order by passengerid asc
) t
)
select
rand(31) as rnd,
passengerid,
array(pclass, name, sex, age, sibsp, parch, ticket, fare, cabin, embarked) as features,
survived
from
train_quantified
;
train_rfの内容はこのようになっています。
//> select rnd,passengerid,features,survived from train_rf limit 1;
+---------------------+--------------+----------------------------------------------+-----------+--+
| rnd | passengerid | features | survived |
+---------------------+--------------+----------------------------------------------+-----------+--+
| 0.7314156962376819 | 1 | [3.0,0.0,0.0,22.0,1.0,0.0,0.0,7.25,0.0,0.0] | 0 |
+---------------------+--------------+----------------------------------------------+-----------+--+
quantifyによりデータを定量化します。文字列の入力についても変換可能なものは数値に変換されます(例えば、sexなど)。randは乱数を出力します。クロスバリデーション時など、サンプリングする際に利用します。arrayで指定した説明変数を一つの配列とします。これは、ランダムフォレストの学習用関数(train_randomforest_classifier)が、説明変数をdoubleの配列として受け取ることを前提としているからです。
同様にテストデータも処理していきます。
//>
create table test_rf
as
WITH test_quantified as (
select
quantify(
output_row, passengerid, pclass, name, sex, age, sibsp, parch, ticket, fare, cabin, embarked
) as (passengerid, pclass, name, sex, age, sibsp, parch, ticket, fare, cabin, embarked)
from (
-- need training data to assign consistent ids to categorical variables
select * from (
select
1 as train_first, false as output_row, passengerid, pclass, name, sex, age, sibsp, parch, ticket, fare, cabin, embarked
from
train
union all
select
2 as train_first, true as output_row, passengerid, pclass, name, sex, age, sibsp, parch, ticket, fare, cabin, embarked
from
test_raw
) t0
order by train_first asc, passengerid asc
) t1
)
select
passengerid,
array(pclass, name, sex, age, sibsp, parch, ticket, fare, cabin, embarked) as features
from
test_quantified ;
次に、説明変数のメタ情報を設定します。
//>
select guess_attribute_types(pclass, name, sex, age, sibsp, parch, ticket, fare, cabin, embarked) from train limit 1;
+----------------------+--+
| c0 |
+----------------------+--+
| Q,C,C,Q,Q,Q,C,Q,C,C |
+----------------------+--+
guess_attribute_typesにより、Hivemallが与えられた変数値を「Q:量的」であるか「C:質的」のどちらと認識するかを確認できます。
これを確認すると、「pclass:乗客のグレード」が量的と判断されており、明示する必要があることがわかります。そこで、説明変数の属性を明示しておきたいと思います。
//> set hivevar:attrs=C,C,C,Q,Q,Q,C,Q,C,C;
これは、次のクエリで使用する変数の設定です。
学習
では、モデルを構築していきたいと思います。//>
create table model_rf
AS
select
train_randomforest_classifier(features, survived, "-trees 500 -attrs ${attrs}")
-- as (model_id, model_type, pred_model, var_importance, oob_errors, oob_tests)
from
train_rf;
train_randomforest_classifierの詳しいオプションは、以下のクエリで確認してください。
//> select train_randomforest_classifier(features, survived,'-help') from train_rf;
私が確認した限りこのクエリでは例外ログが多量に出力されますが。何が違うのかよくわかってないです。スミマセンm(_ _)m。hivemallのWEBページかソースなどから確認してください。
気をとりなおして、モデルのimportanceは下記のクエリで確認してみます。
//>
select
array_sum(var_importance) as var_importance,
sum(oob_errors) / sum(oob_tests) as oob_err_rate
from
model_rf;
+-----------------------------------------------------------------------------------+----------------------+--+
| var_importance | oob_err_rate |
+-----------------------------------------------------------------------------------+----------------------+--+
| [117.50, 707.23, 164.99, 729.44, 123.28, 152.52, 745.85, 759.21, 224.85, 137.27] | 0.18518518518518517 |
+-----------------------------------------------------------------------------------+----------------------+--+
予測
ここまでで、データの準備ができたので、予測を行ってみましょう。
//> set hivevar:classification=true;
//>
create table predicted_rf
as
SELECT
passengerid,
predicted.label,
predicted.probability,
predicted.probabilities
FROM (
SELECT
passengerid,
rf_ensemble(predicted) as predicted
FROM (
SELECT
t.passengerid,
-- hivemall v0.4.1-alpha.2 or before
-- tree_predict(p.model, t.features, ${classification}) as predicted
-- hivemall v0.4.1-alpha.3 or later
tree_predict(p.model_id, p.model_type, p.pred_model, t.features, ${classification}) as predicted
FROM (
SELECT model_id, model_type, pred_model FROM model_rf
DISTRIBUTE BY rand(1)
) p
LEFT OUTER JOIN test_rf t
) t1
group by
passengerid
) t2
;
このクエリでは先に指定したtree=500から、予測結果とそのもっともらしさを集計しています。最終的には得られた結果も確認しておきましょう。
//> select passengerid,label,probability,probabilities from predicted_rf limit 5;
+--------------+--------+--------------+----------------+--+
| passengerid | label | probability | probabilities |
+--------------+--------+--------------+----------------+--+
| 892 | 0 | 0.994 | [0.994,0.006] |
| 893 | 0 | 0.674 | [0.674,0.326] |
| 894 | 0 | 0.94 | [0.94,0.06] |
| 895 | 0 | 0.926 | [0.926,0.074] |
| 896 | 1 | 0.506 | [0.494,0.506] |
+--------------+--------+--------------+----------------+--+
最後にKaggleに結果を提出するために、データをファイル出力します。
まず次のクエリで提出用の形式に変換します。
//>
create table predicted_rf_submit
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ","
LINES TERMINATED BY "\n"
STORED AS TEXTFILE
as
SELECT passengerid, label as survived
FROM predicted_rf
ORDER BY passengerid ASC;
次にターミナルからHive上のデータを通常ファイルに変換します。
$ hdfs dfs -getmerge /user/hive/warehouse/titanic.db/predicted_rf_submit predicted_rf_submit_raw.csv
predicted_rf_submitディレクトリ以下のファイルを結合して、predicted_rf_submit_raw.csvとして取得します。
また、結果にはヘッダーが必要なので、追加します。
$ awk 'NR==1{print "PassengerId,Survived"}{print $0}' predicted_rf_submit_raw.csv > predicted_rf_submit.csv
あとは、KaggleのTitanicページ上の「Submit Predictions」から、結果をアップロードしてみてください。
以上で、終了になります。おつかれさまでした。
APPENDIX
HadoopをHomebrewでインストールした場合、hdfsが実行されるたびにWARNログが出力されると思います。これは、native-hadoop libraryがインストールされていないことが原因です。ここでは、そのHadoopをソースからコンパイルし、ライブラリをインストールしていきたいと思います。 コンパイルには、以下が必要になります。- JDK 1.7+
- Maven 3.0 or later
- ProtocolBuffer 2.5.0
- CMake 2.6 or newer (if compiling native code), must be 3.0 or newer on Mac
- Zlib devel (if compiling native code)
$ hadoop version
Hadoop 2.8.0
Subversion https://git-wip-us.apache.org/repos/asf/hadoop.git -r 91f2b7a13d1e97be65db92ddabc627cc29ac0009
Compiled by jdu on 2017-03-17T04:12Z
Compiled with protoc 2.5.0
From source with checksum 60125541c2b3e266cbf3becc5bda666
This command was run using /usr/local/Cellar/hadoop/2.8.0/libexec/share/hadoop/common/hadoop-common-2.8.0.jar
ここから、HadoopのコンパイルにつかったシリアライザProtocol Buffersのバージョンを確認してください。おそらくprotoc 2.5.0となっているかと思います。
HomebrewからProtocolBufferについて検索し、2.5系をインストールします。
$ brew search /proto/
$ brew install protobuf@2.5
$ protoc --version
libprotoc 3.3.0
このままでは、インストール済みのProtocolBufferが優先され、2.5系を使用できません。そこで一度リンクを削除し、2.5系からリンクを再作成します。
$ brew unlink protobuf
$ brew link --force protobuf@2.5
$ protoc --version
libprotoc 2.5.0
Hadoopのソースをダウンロード、コンパイルし、ライブラリをインストールします。
$ wget http://ftp.jaist.ac.jp/pub/apache/hadoop/common/hadoop-2.8.0/hadoop-2.8.0-src.tar.gz
$ tar xvzf hadoop-2.8.0-src.tar.gz
$ pwd
/usr/local/src/hadoop-2.8.0-src/hadoop-common-project/hadoop-common
$ mvn -P native compile
...
$ sudo mv target/native/target/usr/local/lib/* /Library/Java/Extensions/
これで、作業は完了です。最後にWARNログが出なくなっていることを確認します。
$ hdfs dfs -ls /
DATUM STUDIOは、クライアントの事業成長と経営課題解決を最適な形でサポートする、データ・ビジネスパートナーです。
データ分析の分野でお客様に最適なソリューションをご提供します。まずはご相談ください。
Contact
Explore Jobs
関連記事