Contenu connexe
Similaire à JAWSDAYS 2014 ACEに聞け! EMR編 (20)
JAWSDAYS 2014 ACEに聞け! EMR編
- 3. 今日の話は
Hadoop や Mahout を試したいプログラマ向け
• 自己紹介 & 会社紹介
• Mahout on EMR を試す手順
– Mahout プログラムを書いて EMR で実行
• 初心者が知っておくとよいこと
– よくはまる罠・チューニングの考え方
• 話さないこと
– Hadoop や Mahout の詳しい使い方
– Hbase・Hive・Pig on EMR とか
• さんざん紹介されているのでそっちを見てね!
- 5. • 必要なら何でも徹底的にやる研究者
– プログラミング言語・データベース分散
– ゕルゴリズム・機械学習・CV・自然言語
– 名古屋工業大学出身・未踏ソフトウェゕ経験
– 世界を美しく記述することを夢見る35歳
• 人を驚かせるのが好き
– ハードリゕルタイムJavaVM
– 1000台越え構成のペタバイト分散DB
– 秒間1000万クエリ処理できるKVS
– 超多クラス対応の超高速物体認識エンジン
山 口 陽 平
@melleo1978
※あくまでもメージです。
実物に髪の毛はありません。
- 6. • 概要:11年目
– 名古屋工業大学発ベンチャー(2003年)
• 目的:ロボの頭脳を作る
– 知的インターフェイスによる社会の変革
– ソフトウェゕの品質・生産性の向上
• スタッフ:28人
– 役員3人〃正社員11人〃見習い8人
– データ作成5人〃家政婦1人
– IPA未踏ソフトウェゕ経験者多数
• 社風:難しことを楽しく
– 職人〃挑戦〃自由〃昼食・夕食・飲み会は無料
来栖川電算
http://kurusugawa.jp/
弊社が目指すロボ
※写真はあくまでもメージです。
- 7. 来栖川電算
認識技術 や 大規模なデータの分析 をやる会社
• 認識技術
– 情景画像文字認識
– 物体認識
– モーション認識・行動認識
• データ分析
– 対象:映像〃各種センサ〃サービスログ
– 規模:ペタバイト級
• 日々やってること
– ゕルゴリズム・機械学習・特徴量の研究と応用
- 19. EMR
単なる hadoop との違い
• 追加機能
– 分散フゔイルシステムとして S3 も選べる
– AWS 環境に合わせてチューニングされてる
• 選ぶだけでンストールできる機能
– 構造化データの処理:Hive〃Hbase〃Pig
– 監視:Ganglia
• クラスタの管理
– 起動:ノード数を入力するだけ
– 監視・操作:Web・CLI・SDK
- 20. EMR
料金 … よく考えると安い!
• EC2 料金 + EMR 料金(1時間単位)
– EMR 料金:EC2 料金(オンデマンド)× 1/4 ~ 1/7
– リザーブドやスポットで節約可能
• 例:cc2.8xlarge … 2.9$/時間
– 買うと 30 万円 ⇒ 1000 時間
– 4 台時間/実験 ⇒ 250 実験
– 普段はローカルの小さなデータで実験。たま
に大きなデータで実験するときだけなら安い
– スポット使えば 20% くらいは節約できそう
- 23. Mahout
いろんなゕルゴリズムが実装されている
• 1.0 になるともっと充実!
Collaborative Filtering
User-Based Collaborative Filtering
Item-Based Collaborative Filtering
Matrix Factorization with Alternating Least Squares
Matrix Factorization with Alternating Least Squares on Implicit Feedback
Weighted Matrix Factorization, SVD++, Parallel SGD
Classification
Logistic Regression
Naive Bayes / Complementary Naive Bayes
Random Forest
Hidden Markov Models
Multilayer Perceptron
Clustering
Canopy Clustering
k-Means Clustering
Fuzzy k-Means
Streaming k-Means
Spectral Clustering
Dimensionality Reduction
Singular Value Decomposition
Lanczos Algorithm
Stochastic SVD
Principal Component Analysis
Topic Models
Latent Dirichlet Allocation
Miscellaneous
Frequent Pattern Mining
RowSimilarityJob
ConcatMatrices
Collocations
- 34. 出力形式
2種類のシーケンスフゔル(分割OK)
• K-Means に渡すパスは親階層
クラスタデータ
初期パス target/output/clusters-0/part-XXXXX
中間パス target/output/clusters-X/part-r-XXXXX
最終パス target/output/clusters-X-final/part-r-XXXXX
形式 {Key:Int, Value:Cluster}
意味 {Key:クラスタ番号, Value:クラスタの中心と半径}
ベクトルデータの分類結果
パス target/output/clusteredPoints/part-m-XXXXX
形式 {Key:Int, Value:WeightedVector<Double>}
意味 {Key:クラスタ番号, Value:クラスタリング対象ベクトル}
- 35. pom フゔル
Eclipse で Maven プロジェクトを作成
• Mahout 0.8 への依存関係を追加
• 依存先を jar に含める設定を追加
– これから作る main クラスの完全修飾名
<dependencies>
<dependency><groupId>org.apache.mahout</groupId><artifactId>mahout-core</artifactId><version>0.8</version></dependency>
<dependency><groupId>org.apache.mahout</groupId><artifactId>mahout-math</artifactId><version>0.8</version></dependency>
<dependency><groupId>org.apache.mahout</groupId><artifactId>mahout-examples</artifactId><version>0.8</version></dependency>
</dependencies>
<build><plugins><plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs>
<archive><manifest><mainClass>jp.kurusugawa.pacth_clustering</mainClass></manifest></archive>
</configuration>
<executions><execution>
<id>make-assembly</id><phase>package</phase><goals><goal>single</goal></goals>
</execution></executions>
</plugin></plugins></build>
- 36. main プログラム
ベクトルデータを書き込む
• LongWritableとVectorWritableを使う
private static void downloadPatches(Configuration aConfiguration, Path aWorkingInputPatchesPath, Path aPatchesPath, int aColumnCount)
throws IOException {
SequenceFile.Writer tOutput = openSequenceFileWriter(aConfiguration, aWorkingInputPatchesPath, LongWritable.class,
VectorWritable.class);
try {
LongWritable tRowIndexWritable = new LongWritable();
VectorWritable tRowWritable = new VectorWritable();
DataInputStream tInput = openDataInputStream(aConfiguration, aPatchesPath);
try {
long tRowCount = getFileLength(aConfiguration, aPatchesPath) / aColumnCount;
for (long tRowIndex = 0L; tRowIndex < tRowCount; tRowIndex++) {
Vector tRow = new RandomAccessSparseVector(aColumnCount);
for (int tColumnIndex = 0; tColumnIndex < aColumnCount; tColumnIndex++)
tRow.setQuick(tColumnIndex, tInput.readUnsignedByte());
tRowIndexWritable.set(tRowIndex);
tRowWritable.set(tRow);
tOutput.append(tRowIndexWritable, tRowWritable);
}
} finally { IOUtils.closeQuietly(tInput); }
} finally { IOUtils.closeQuietly(tOutput); }
}
- 37. main プログラム
クラスタデータを書き込む
• TextとKlusterを使う
• 「似ている」の基準:マンハッタン距離
private static void downloadClusters(Configuration aConfiguration, Path aWorkingInputClustersPath, Path aPatchesPath, int aColumnCount,
int aClusterCount) throws IOException {
SequenceFile.Writer tOutput = openSequenceFileWriter(aConfiguration, aWorkingInputClustersPath, Text.class, Kluster.class);
try {
DataInputStream tInput = openDataInputStream(aConfiguration, aPatchesPath);
try {
int tClusterCount = (int) Math.min(aClusterCount, getFileLength(aConfiguration, aPatchesPath) / aColumnCount);
for (int tClusterIndex = 0; tClusterIndex < tClusterCount; tClusterIndex++) {
Vector tCenter = new RandomAccessSparseVector(aColumnCount);
for (int tColumnIndex = 0; tColumnIndex < aColumnCount; tColumnIndex++)
tCenter.setQuick(tColumnIndex, tInput.readUnsignedByte());
Kluster tCluster = new Kluster(tCenter, tClusterIndex, new ManhattanDistanceMeasure());
tOutput.append(new Text(tCluster.getIdentifier()), tCluster);
}
} finally { IOUtils.closeQuietly(tInput); }
} finally { IOUtils.closeQuietly(tOutput); }
}
- 38. main プログラム
K-Means でクラスタリング
• 出力先を削除する(やらないとこける)
• 入力元と出力先のパスを指定し実行する
private static void run(Path aPatchesPath, Path aClustersPath, Path aWorkingInputPatchesPath, Path aWorkingInputClustersPath, Path
aWorkingOutputPath, int aColumnCount, int aClusterCount) throws IOException, InterruptedException, ClassNotFoundException {
Configuration tConfiguration = new Configuration();
System.out.println("download patches");
HadoopUtil.delete(tConfiguration, aWorkingInputPatchesPath);
downloadPatches(tConfiguration, new Path(aWorkingInputPatchesPath, "part-00000"), aPatchesPath, aColumnCount);
System.out.println("download clusters");
HadoopUtil.delete(tConfiguration, aWorkingInputClustersPath);
downloadClusters(tConfiguration, new Path(aWorkingInputClustersPath, "part-00000"), aPatchesPath, aColumnCount, aClusterCount);
System.out.println("clustering");
HadoopUtil.delete(tConfiguration, aWorkingOutputPath);
KMeansDriver.run(tConfiguration, aWorkingInputPatchesPath, aWorkingInputClustersPath, aWorkingOutputPath, new
ManhattanDistanceMeasure(), 0.001D, 10, true, 0D, false);
System.out.println("upload clusters");
uploadClusters(tConfiguration, getWorkingOutputClustersPaths(tConfiguration, aWorkingOutputPath), aClustersPath, aColumnCount);
}
- 39. main プログラム
出来上がったクラスタデータを読み込む
• IntWritable・ClusterWritableを使う
private static void uploadClusters(Configuration aConfiguration, Path[] aWorkingOutputClustersPaths, Path aClustersPath, int
aColumnCount) throws IOException {
DataOutputStream tOutput = openDataOutputStream(aConfiguration, aClustersPath);
try {
for (Path tClustersPath : aWorkingOutputClustersPaths) {
SequenceFile.Reader tReader = openSequenceFileReader(aConfiguration, tClustersPath);
try {
IntWritable tClusterIndex = new IntWritable();
ClusterWritable tClusterWritable = new ClusterWritable();
while (tReader.next(tClusterIndex, tClusterWritable)) {
Vector tCenter = tClusterWritable.getValue().getCenter();
for (int tColumnIndex = 0; tColumnIndex < aColumnCount; tColumnIndex++) {
long tValue = Math.round(tCenter.getQuick(tColumnIndex));
tOutput.write(tValue < 0L ? 0 : tValue > 255L ? 255 : (int) tValue);
}
}
} finally { IOUtils.closeQuietly(tReader); }
}
} finally { IOUtils.closeQuietly(tOutput); }
}
- 40. main プログラム
フゔルシステムにゕクセスするユーテゖリテゖ
• パスからフゔルシステムを決定しない
と EMR で実行したときにこける
– hdfs://、s3://、s3n:// が混在するから
private static FileSystem getFileSystem(Configuration aConfiguration, Path aPath) throws IOException {
return FileSystem.get(aPath.toUri(), aConfiguration); }
private static FileStatus[] getFileStatuses(Configuration aConfiguration, Path aPath, PathFilter aPathFilter) throws IOException {
return getFileSystem(aConfiguration, aPath).listStatus(aPath, aPathFilter); }
private static long getFileLength(Configuration aConfiguration, Path aPath) throws IOException {
return getFileSystem(aConfiguration, aPath).getContentSummary(aPath).getLength(); }
private static DataInputStream openDataInputStream(Configuration aConfiguration, Path aPath) throws IOException {
return getFileSystem(aConfiguration, aPath).open(aPath); }
private static DataOutputStream openDataOutputStream(Configuration aConfiguration, Path aPath) throws IOException {
return getFileSystem(aConfiguration, aPath).create(aPath); }
private static SequenceFile.Reader openSequenceFileReader(Configuration aConfiguration, Path aPath) throws IOException {
return new SequenceFile.Reader(getFileSystem(aConfiguration, aPath), aPath, aConfiguration); }
private static SequenceFile.Writer openSequenceFileWriter(Configuration aConfiguration, Path aPath, Class<?> aKeyClass, Class<?>
aValueClass) throws IOException {
return new SequenceFile.Writer(getFileSystem(aConfiguration, aPath), aConfiguration, aPath, aKeyClass, aValueClass); }
- 43. main プログラム
EMR で実行する前にパスを指定できるように変更
• 両環境で実行できるプログラム
– 中間データのパスも指定できると便利
public static void main(String[] aArguments) throws IOException, InterruptedException, ClassNotFoundException {
Path tPatchesPath = new Path("src/test/resources/patches/patches.bin");
Path tClustersPath = new Path("src/test/resources/patches/clusters.bin");
Path tWorkingInputPatchesPath = new Path("target/input/patches");
Path tWorkingInputClustersPath = new Path("target/input/clusters");
Path tWorkingOutputPath = new Path("target/output");
int tColumnCount = 31 * 31; int tClusterCount = 256;
if (aArguments.length > 0) {
tPatchesPath = new Path(aArguments[0]);
tClustersPath = new Path(aArguments[1]);
tWorkingInputPatchesPath = new Path(aArguments[2]);
tWorkingInputClustersPath = new Path(aArguments[3]);
tWorkingOutputPath = new Path(aArguments[4]);
tColumnCount = Integer.parseInt(aArguments[5]);
tClusterCount = Integer.parseInt(aArguments[6]);
}
run(tPatchesPath, tClustersPath, tWorkingInputPatchesPath, tWorkingInputClustersPath, tWorkingOutputPath, tColumnCount,
tClusterCount);
}
- 48. クラスタを起動
EMR Management Console
• Cluster Configuration
– クラスタ名とログ出力先を設定する
• ログ出力先:s3n://バケット名/パス/
• Software Configuration
– Hadoop のバージョンを設定する
• Mahout 0.8 を使うには Hadoop 1.0.3
– 追加インストールするものを選ぶ
• 使わないなら消しておく。
- 49. クラスタを起動
EMR Management Console
• Hardware Configuration
– インスタンスタイプと数を設定する
• マスター(main プログラムが動くノード):1個
• コゕ(タスクも処理するデータノード):0個~
• タスク(タスクを処理するノード):0個~
– 1台構成(マスターのみ)
– 複数台構成(マスターと他のノード)
• マスターはタスクを処理しないのでしょぼくていい
• 1時間弱で終わるように選ぶと安い
• 1時間以上かかる場合に Spot を選ぶと危険な場合も
• EMR は計算用として使うのがおすすめ
- 50. クラスタを起動
EMR Management Console
• Steps
– Custom JAR を選んでパスと引数を設定する
• 中間データもすべて引数で設定できると便利
• 中間データのパスを S3 にしておくと便利
• デバッグ中は終了しないとすると便利
s3n://jawsdays2014/data/book_patches.bin
s3n://jawsdays2014/data/book_clusters.bin
hdfs:///input/book_patches
hdfs:///input/book_clusters
hdfs:///output
961
256
s3n://jawsdays2014/executables/patch_clustering-0.0.1-SNAPSHOT-jar-with-dependencies.jar
- 53. クラスタを監視
EMR Management Console
• Cluster
– クラスタの設定を確認できる
• Summary〃Configuration Details〃
Security/Network〃Hardware
• Monitoring ← ボトルネックが分かる
– クラスタの負荷を様々な観点で確認できる
• Cluster Status〃Map/Reduce〃Node Status〃IO
• Steps ← 失敗した処理が分かる
– ステップの進捗を様々な粒度で確認できる
• Step〃Job〃Task
- 59. EMR で実行
EMR Management Console
• Steps > All Jobs > Tasks
– ジョブとステップのログが分かる。
• ステップ(main プログラム)から起動された
ジョブ、それを構成するタスクが分かる。
- 64. チューニング
今回の main プログラムのダメなところ
• S3 ⇔ HDFS
– マスターだけで動くので
• スケールしない
• マスターが変換中にスレーブが遊んでる
– スケールさせるには
• Mapper として実装
• 入力フゔイルを事前に分割 or 分割可能に
• Mahout 用入力が無駄にでかい
– Byteがdoubleになっちゃう ⇒ 圧縮で解決
– K-Means 簡単だし、自分で実装してもいいよね
- 67. まとめ
こわがらずに試してみよう
• Hadoop クラスタの起動が楽
– 計算時のみ起動する使い方がお勧め
• Hadoop クラスタの監視も楽
– Management Consoleだけでも結構分かる
• Mahout もそんなに難しくない
– 数十行で程度で試せる
– 単なる集計ではできないことができる
– 機械学習やると Hadoop 使ってる感じがする