word2vecで得られたベクトルを主成分分析してみる
概要
前回の記事(以下のリンク)で、word2vecを利用して、単語をベクトルへ変換しました。
その時は、とりあえず、200次元のベクトルとして学習させましたが、どんな感じに学習されているのか(次元数が多すぎたりしないのかなど)興味がわいたので、
主成分分析を行って調べて見ることにします。
準備
- 主成分分析用途
- pip install sklearn
- グラフ作成用途
- pip install pandas
- pip install matplotlib
主成分分析って何?
元のベクトルが持つ情報をなるべく欠損することなく縮約する手法です。
何が嬉しいかと言うと、例えば10次元のベクトルが2次元や3次元で済めば、保存容量も少なくて済みますし、可視化も簡単です。
詳しい解説は、以下のリンクを参照してください。
http://www.statistics.co.jp/reference/software_R/statR_9_principal.pdf
手順
手順と言うほどの長さではないので、コードを直接読んで頂いた方が早いと思います(コメントもつけてます)。
私は、元のword2vecの次元を200で作成しているので、主成分分析した後の次元も200で揃え、 累積の寄与率を計算しています。
累積の寄与度が1に近くほど元のベクトルから情報の欠損がないことを示しています。
#!/usr/bin/env python # coding: utf-8 from sklearn.decomposition import PCA from gensim.models import Word2Vec import matplotlib import matplotlib.pyplot as plt import pandas as pd # word2vecのモデルを読み込みます。 model_file_name = "jawiki_wl_all.zip_200_1.model" model = Word2Vec.load_word2vec_format(model_file_name, binary=True) # 元が200次元あるので、変換先のベクトルも200次元にしておきます。 pca = PCA(n_components = 200); # word2vecのモデルから取り出した全部の単語ベクトルの配列を引数として、主成分分析を行います。 pca.fit_transform(map(lambda k : model[k], model.vocab.keys())); ccr = 0.0; #累積寄与率 (0で初期化) #(1行目) 最終的に得る結果 (idxは添え字(グラフ描画のために1始まり)) #(2行目) 各ratioに対して #(3行目) ccrへratioを加算して行く graph_input = [[idx+1,ccr] \ for idx,ratio in enumerate(pca.explained_variance_ratio_) \ for ccr in [(lambda x,y:x+y)(ccr,ratio)]] # pandasとmatplotlibによるグラフ描画 df = pd.DataFrame(graph_input, columns=list('nr')) df.plot(x=['n'],y=['r'],kind='scatter') plt.title(u'cumulative contribution ratio', size=16) plt.show();
結果
結果は、以下の通りです。縦軸が累積寄与率、横軸が主成分(寄与率の高い順)です。
グラフの立ち上がりが急であればあるほど、より少ない次元で表現可能であることを示し、グラフが直線になるほど、情報は均等に分散していることを示します。
25次元くらいまでは、ぐっと立ち上がりますが、その後ゆるやかな増加になっています。
特定の主成分に偏りすぎることなく綺麗に分布しているように思えます。
pythonのgensimライブラリを利用して日本語wikipediaの全文からword2vecを学習させるまでの全手順
Word2Vecを計算するまでの全手順を書いておこうと思います。
ご存知の方も多いと思いますが、Word2Vecは、単語をvector化して扱う技術です。
以下の解説がわかりやすいと思います。
Vector Representations of Words | TensorFlow
同じような使われ方をする単語同士が近くなります。
例えば、「夏」という単語の近くには、「冬」が配置されます。
さらに、vector化することで、単語の足し引きができるようになります。
例えば、「叔母」ー「女」+「男」=「叔父」となります。
前置きは、これくらいにして、実際に計算するまでの手順に移りたいと思います。
Word2Vecを計算するまでの概要
学習に利用する文章の収集
- 今回は、wikipediaの全文から学習させます。
わかち書き
- 文章のままでは学習できないので、単語に分割します。
Word2Vecのモデルの学習
- Pythonのgensimライブラリを利用して学習させます。
手順の詳細
学習に利用する文章の収集
- wikipediaの全文(bz2形式)をダウンロードします。
- ファイルは以下のリンクから辿った先にあります。
- Wikipedia:データベースダウンロード - Wikipedia
- ファイル名が「jawiki-latest-pages-articles.xml.bz2」が最新のダンプです。
- もしくは、以下のコマンドを実行することで、カレントディレクトリに保存できます。
- ファイルは以下のリンクから辿った先にあります。
- w2vの解析に必要な日本語の部分を取得する。
#!/usr/bin/env python # -*- coding: utf-8 -*- #ja.wikipediaのdumpから<page>...</page>の部分の非ascii文字(日本語を期待)を切り出しファイルへ出力する。 import string import bz2 import sys import os import re def simple_ja_filter(ifn,ofn): try : MAX_FILE_SIZE = 64 * 1024 * 1024; #1ファイルが大体、数十Mbyteくらいになるようにしておく TEXT_NUM_IN_A_ZIPFILE = 10000; table = string.maketrans( "".join(map (lambda x : chr(x), #(文字コードから文字へ変換する.) filter (lambda x : x not in range(48,58),#数字(0-9)は除いて、 [i for i in range(32,127)]))), #print可能なascii文字は、 "".join(chr(127) for i in range(127 - 32 - 10)) #削除する (注:正しくは、del文字に置き換え) ) with bz2.BZ2File(ifn) as bz2_f : buf = ""; is_in_page = False; cnt = 0; of = None; for ln in bz2_f: #1行読み出し ln = ln.strip(); if cnt % TEXT_NUM_IN_A_ZIPFILE == 0 or of != None and of.tell() > MAX_FILE_SIZE: if of != None: of.close(); of = open( ofn + "_" + str(cnt / TEXT_NUM_IN_A_ZIPFILE + 1).zfill(4) + ".txt", "w" ) if ln == "<page>": #<page>タグを見つける buf = "" is_in_page = True; continue; if ln == "</page>": #</page>までを1ページとして扱う is_in_page = False; if len(buf) != 0: of.write(buf) cnt = cnt + 1; if cnt % 10000 == 0: #どれくらい進んだかを出力する print str(cnt) + " articles done"; continue; if is_in_page: #数字を除いてascii文字を削除する non_ascii = ln.translate(table).replace(chr(127),""); #空の行、数字のみの行は省く if len(non_ascii) != 0 and re.match(r"^[0-9]+$",non_ascii) == None: buf += non_ascii + os.linesep continue; if of != None: #最後に残っている内容を書き出し of.close(); except Exception as e: print e if __name__ == '__main__': if len(sys.argv) < 3: print ("usage : [wiki dump file name(bz2)] [output file prefix]") sys.exit(); ja_wiki_dump_fn = sys.argv[1] out_zip_fn = sys.argv[2] simple_ja_filter(ja_wiki_dump_fn,out_zip_fn)
分かち書き
- MeCabを利用して単語に分割します。
cat [入力となるテキストファイル名] | mecab -b 100000000 | grep -e 名詞 -e 動詞 -e 形容詞 -e 形容動詞 -e 副詞 | grev -v 助動詞 | cut -f 1 > [結果の出力先]
Word2Vecのモデルの学習
- Gensimのmodelにword2vecもあるのでそれを利用します。
- 以下のプログラムを走らせると学習し、モデルが得られます。
- (長いので本文の最後においておきます)
- ただ、上記のプログラムは大量のメモリを食います。wikipedia全文だと数十Gbyteほど。ですので、AWSのEC2(r4.4xlarge = 122Gbyteのメモリ)を利用します。
- word2vecの学習のプログラムは、以下の通りです。
- プログラムのインプットは、分かち書きして得られた複数の文書をzipで固めたものです。
- zipで固めずに、1つづつファイルを読み込んでも良いのですが、容量を食うのでまとめてしまいました(この辺は好みの問題かと思います)。
- プログラムのインプットは、分かち書きして得られた複数の文書をzipで固めたものです。
#!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys import pickle import zipfile import collections from gensim.models import Word2Vec """ <<入力(引数)>> 引数は、zipfileの名前を期待する。 zipfileの構造は以下を期待する archive内の各memberは、単語(word)が改行(\n)で区切られている 例: archiveの中身(member)は以下の様になっており jawiki_word_list_0001 jawiki_word_list_0002 ... 各memberの中身は以下の様担っている word_1 word_2 word_3 ... <<出力>> wordのリスト [word,word,word...] """ def read_word_list_from_zip(filename): try : word_list = [] with zipfile.ZipFile(filename) as archive: for name in archive.namelist(): print name with zipfile.ZipFile.open(archive,name) as zf: for word in zf: word_list.append(word.strip()) return word_list except IOError as ioe: print ioe; #File Open失敗 """ 利用する単語の集合を規定する。 今回は、word_listに登場するwordの内、上からvocabulary_size分を利用する単語とする。 (全部利用するならこの処理は要らない) """ def build_dic(word_list,vocabulary_size): #vocabulary size分の単語を選定する counter = collections.Counter(word_list); count = [['UNK',-1]]; #'UNK'は、UNKnown wordsで、未知語 count.extend(counter.most_common(vocabulary_size - 1)); #-1するのは、'UNK'の文 #辞書の作成 dictionary = dict(); for word,_ in count: dictionary[word] = len(dictionary); #今回は使いませんが、逆引き辞書を作る場合 #reverse_dictionary = dict(zip(dictionary.values(),dictionary.keys())); return dictionary """ word_list (単語の1次元配列)から利用する単語のみを抜粋し、 sentences (単語の2次元配列)に変換する """ def wordlist2sentences(word_list,dic): MAX_SENTENCE_LEN = 30; sentences = []; sentence = []; cnt = 0; for word in filter(lambda word : word in dic, word_list): cnt = cnt + 1; if cnt % MAX_SENTENCE_LEN == 0: sentences.append(sentence); sentence = []; sentence.append(word); if len(sentence) > 0: sentences.append(sentence); return sentences; def w2v_learn(sentences,size,min_count): model = Word2Vec( sentences, size=size, window=5, min_count=min_count, workers=4) return model; def output_model_and_dic(w2v_model,fn,size,min_count,dic): output_file_name = os.path.basename(fn) + "_" + \ str(size) + "_" + \ str(min_count) + ".model" w2v_model.save_word2vec_format(output_file_name,binary=True) ''' dic_file_name = os.path.basename(zip_file) + ".dic" with open(dic_file_name, 'w') as f: pickle.dump(dic,f) ''' def main(zip_file_name): #データの読み出し word_list = read_word_list_from_zip(zip_file_name); print ("reading done"); #利用する単語の選定 dic = build_dic(word_list,100000); print ("build dic done") #Word2Vec様のインプット生成 sentences = wordlist2sentences(word_list,dic); #Word2Vecのモデルの学習 print ("start learning") W2V_SIZE = 200; W2V_MIN_COUNT = 3; w2v_model = w2v_learn(sentences,W2V_SIZE,W2V_MIN_COUNT); print ("learning done") #書き出し output_model_and_dic(w2v_model,zip_file_name,W2V_SIZE,W2V_MIN_COUNT,dic); if __name__ == '__main__': if len(sys.argv) < 2: print ("usage : [zip file]") sys.exit(); zip_file_name = sys.argv[1] main(zip_file_name);
遊んでみる
- word2vecを計算するコードは以下になります。
- 「夏」に一番近い単語だと、「春」になりました。
- 「叔母」 - 「女」 + 「男」=「叔父」となっているので、いい感じですね。
#!/usr/bin/env python # coding: utf-8 from gensim.models import Word2Vec import json model_file_name = "jawiki_wl_all.zip_200_1.model" model = Word2Vec.load_word2vec_format(model_file_name, binary=True) def most_similar(pos_list,neg_list): try : return map(lambda t: (t[0].encode('utf-8'),t[1]), model.most_similar( map(lambda w : unicode(w,'utf-8'),pos_list), map(lambda w : unicode(w,'utf-8'),neg_list), topn=10)); except Exception as e: print (str(e)) #'夏'に近い単語を上位10個出力する print "「夏」に一番近いのは… " for t in most_similar(['夏'],[]): print t[0], t[1] print "" print "「叔母」- 「女」+ 「男」 = ?" for t in most_similar(['叔母','男'],['女']): print t[0], t[1]
あとがき
- 今回、名詞、動詞や形容詞も放り込みましたが、利用する単語の取捨選択は、要調整です。
- 単語は、原型の方を選択して、学習させた方が上手くいくと思います。
AWSのlambda上でMeCabを実行する (他のバイナリへも応用可)!!
日本語の解析をする場合、とりあえず形態素解析を実施することになると思います。
手っ取り早く動かすのであれば、ローカルで動作させれば良いですが、サーバ側で処理することも多いかと思います。
AWS lambda + APIGateway で動作させることが出来れば、便利なので、その事前準備として、lamdab上で形態素解析を行うと言う話です。
日本語の形態素解析といえば、MeCabが定番なのではないでしょうか。
MeCab: Yet Another Part-of-Speech and Morphological Analyzer
ただ、mecabはc/c++で実装されており、lambdaで動作させるためには、lambdaで利用されているCPUが解釈できるバイナリを準備する必要があります。
lambdaが動作する環境は、AMIとしてAWSが公開しています。
Lambda 実行環境と利用できるライブラリ - AWS Lambda
要は、このAMIからEC2インスタンスを一旦起動し、立ち上げたEC2以上でmecabをバイナリへコンパイルし、その成果物をlambdaで利用すれば良いことになります。
また、lambdaでプログラムを実行した場合、/var/task配下で実行されることになります。
今回は、/var/task/mecab配下へmecabのバイナリや辞書をおくことにします。
以下に具体的な手順を示します。
- lambdaのAMIからEC2インスタンスを起動します。
- ログイン後、取り敢えず環境をアップデートしておきます。
- sudo yum update
- Mecabのコンパイルに必要なソフトウェアをインストールします。
- MeCabのダウンロード (ここはEC2内でも外でも大丈夫です。適宜DLしてEC2インスタンスへ持っていきましょう。)
- MeCabをインストールするためのディレクトリを作成します。
- MeCabのビルド
- IPAdicのビルド
- できたバイナリ(/var/task/mecab配下)を、一旦ローカルのコンピュータへコピーしておきます。
- 確認します。
あらゆる 連体詞,*,*,*,*,*,あらゆる,アラユル,アラユル 現実 名詞,一般,*,*,*,*,現実,ゲンジツ,ゲンジツ を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ 全て 名詞,副詞可能,*,*,*,*,全て,スベテ,スベテ 自分 名詞,一般,*,*,*,*,自分,ジブン,ジブン の 助詞,連体化,*,*,*,*,の,ノ,ノ 方 名詞,非自立,一般,*,*,*,方,ホウ,ホー へ 助詞,格助詞,一般,*,*,*,へ,ヘ,エ ねじ曲げ 動詞,自立,*,*,一段,連用形,ねじ曲げる,ネジマゲ,ネジマゲ た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ の 名詞,非自立,一般,*,*,*,の,ノ,ノ だ 助動詞,*,*,*,特殊・ダ,基本形,だ,ダ,ダ 。 記号,句点,*,*,*,*,。,。,。 EOS
lambda上での実行
- AWS lambda functionを作成します。
- 作成手順は、AWSの公式ドキュメントなどを参考にしてください。
- 作成したlambda functionのコードをローカルへコピーします。
- index.jsを編集します。
- サンプルを下の方に置いておきます。
- ファイルをlambdaへ転送します。
- 実行する
- 今までの手順が正しくできていると、以下の結果がlambdaのログに吐かれていると思います。
- 「あらゆる 連体詞,*,*,*,*,*,あらゆる,アラユル,アラユル」
- 今までの手順が正しくできていると、以下の結果がlambdaのログに吐かれていると思います。
- 以下にindex.jsのサンプルを置いておきます(後述の注意事項も合わせて参照願います)。
'use strict'; const exec = require('child_process').exec; const fs = require('fs') exports.handler = (event, context, callback) => { /* OSコマンドインジェクションは避けたいので、 以下のように、変動要素(解析文字列)を渡すのは危険 const cmd = 'echo "あらゆる" | /var/task/mecab/bin/mecab' */ fs.writeFileSync("/tmp/input.txt","あらゆる"); const cmd = '/var/task/mecab/bin/mecab /tmp/input.txt'; /* lambda実行時は、/tmp配下以外は書き込めないので注意 */ exec(cmd , (err, stdout, stderr) => { if (err) { console.error(err); callback(null, "error"); return; } console.log(stdout); //ここでmecabの実行結果が表示される。 callback(null, "done"); }); };
- 注意事項
execコマンドは、便利ですが、最終的に sh -c "/var/task/mecab/bin/mecab" と言うように入力がシェルに渡ります。
つまり OS コマンド インジェクションに注意しないといけません。
今回は、固定文字列(「あらゆる」)なので問題ないですが、実際は、任意の文字列を渡したいと思います。
その場合、外部からの入力がコマンドラインのパラメータに渡らないように注意してください。
例えば、形態素解析したい文字列は、ファイルへ書き出しておき(input.txt)、execコマンドで実行するのは、「mecab input.txt 」と言うように固定してしまうことです。
もちろん、正攻法で、サニタイズするのもありです。
mecabを実行しようとしてエラーが出た場合
- lambdaを実行しようとして、libmecab.so.2 がない(no such file or directory)と言って怒られた場合
- それは、EC2で、make installした時に、しれっとリンク (ln -s -f libmecab.so.2.0.0 libmecab.so.2) が作成されていたのに、lambdaでは作成していないからです。
AWS: cognitoのuser poolで管理者または開発者が払いだしたIDとパスワードを利用してブラウザからサーバへログインする
今回は、ユーザの登録は管理者がAWSのコンソールなどで実施し、一般ユーザはログインだけするケースです。
ブラウザからのcognitoに接続して認証情報(access token等)を取得することが目的です。
具体的には、以下の「管理者または開発者によって作成されるユーザーの認証フロー」の部分を実装します。
docs.aws.amazon.com
AWSのコンソールを使ってUser Pool の設定をする。
docs.aws.amazon.com
User Poolの設定では、emailの登録を必須にする(orしない)やパスワードの長さや文字種別などを設定します。
(ここは、ドキュメント通りで特に迷わ無いかと思います。)
今回は、emailのみ必須にしました。
User Poolの画面で、ユーザを作成する。
ここは、コンソールの画面の流れに従います。
AWSコンソールで作成した場合は、以下のリンクの「Force Change Password」状態です。
docs.aws.amazon.com
Identity Poolを作成し、User Poolを結びつけます。
ここは、以下のドキュメント通りに実施すれば迷うことは無いかと思います。
ユーザープールをフェデレーテッドアイデンティティと統合する - Amazon Cognito
認証に必要なjavascriptライブラリを集める
基本的には、以下のリンクのステップを実施していくだけです。
Amazon Cognito ID SDK for JavaScript でユーザープールを使用するようにセットアップする - Amazon Cognito
Stanford JavaScript Crypto Library (sjsc.js)は、自前でビルドが必要になります。
sjclをダウンロードしたフォルダに移動して、以下のコマンド(2つ)を実行します。
./configure --with-codecBytes make
sjcl.jsが更新されるので、出来上がった新しいsjcl.jsを使用します。
javaが無いと失敗するので、それっぽい警告が出た場合は、javaをインストールしましょう。
homebrewが既にインストールされていれば、以下のコマンドです。
brew cask install java
homebrewが無い場合は、手前味噌ですが、以下の記事のhomebrewのインストールの箇所をご参照ください。
marmarossa.hatenablog.com
また、amazon-cognito-identity.min.js が、初期化で失敗したので、以下からコードを一式持ってきました。
github.com
(細かいですが、ブラウザで動作させるために、importやexportは消さないと動きません。)
amazon-cognito-identity.min.jsそのままだと、なぜか、以下の初期化時に失敗しました…(私だけ?)
new AWSCognito.CognitoIdentityServiceProvider()
ブラウザ側の実装
MFAの設定を省略すると、実装するのは、大きく以下の2ステップです。
- ユーザにIDと仮パスワードを入力して貰いcognito側とやり取りする。
- 新しいパスワードをcognito側に送信し、「confirmed」状態にする。
AWS側の提供している以下のコード例は、さっぱりしているので、背後で何が起きているのかを付け足します。
docs.aws.amazon.com
まずは、ステップ1のユーザにIDと仮パスワードを入力してもらい、cognitoとやり取りする部分です。
ブラウザとcognitoの間は、 Remote Password (SRP)プロトコル でやり取りしており、実際にはパスワードは送信されません。
(APIには、パスワードを送信するものも用意されていますが、安全な(例えばman in middle攻撃を受け無い)通信路が確保されていることが利用条件です。)
SRP自体は、以下のwikiが詳しいです。
Secure Remote Password protocol - Wikipedia
個別に拾ってきたCognitoUser.jsの中でSRPの全体を制御して、具体的な計算は、AuthenticationHelper.js内で実施しており、
上記のwikiと照らし合わせると大体何をやっているのかが掴めます。
サンプルにすると以下の通りで、冒頭の「管理者または開発者によって作成されるユーザーの認証フロー」のステップだと1から5に相当します。
var poolData = { UserPoolId : '{{YOUR USER ID}}', ClientId : '{{YOUR Client ID}}', Paranoia : 7 //7である必要性はないが、0だと古いIEなどでセキュアでない。 }; var userPool = new CognitoUserPool(poolData); var userData = { Username : '{{username}}', //作成したユーザ名です。 Pool : userPool }; var cognitoUser = new CognitoUser(userData); var authenticationData = { Username : '{{username}}',//作成したユーザ名です。 Password : '{{password}}', //作成したユーザのパスワードです。 }; var authDetails = new AuthenticationDetails(authenticationData); cognitoUser.authenticateUser(authDetails,{ onFailure : function(err) { //何か処理に失敗した場合に呼ばれる。 }, onSuccess : function(result) { //Confirmed状態で認証に成功した場合に呼ばれる。 //token等はresultに格納されている。 }, newPasswordRequired: function(userAttributes,requiredAttributes) { //Force Change Password状態で、認証に成功した場合に呼ばれる。 //この後、ユーザから新しいパスワードを受けとり更新する必要がある。 } });
次は、ステップ2の新しいパスワードをcognitoへ送る部分です。
注意点として、CognitoUserは、先ほどのインスタンスを使ってください。
CognitoUserクラスは、cognitoとやり取りするときに、内部でSessionを自動付与して送付しており、再度作成し直すと、Sessionは当然クリアされているのでエラーになります。
//第2引数は、追加で設定が必要な属性(誕生日や性別など)がある場合に指定します。 //今回は、emailのみ必須の属性で、管理コンソール側で設定してしまっているのでnullとしています。 cognitoUser.completeNewPasswordChallenge(new_password,null,{ onFailure : function(err) { //なにか失敗した }, onSuccess : function(result) { //成功した(confirmedに以降した) } //MFAを設定する場合は略 });
以上でMFAの設定をしない場合は、無事にトークンが取得できたと思います。
参考
公式のドキュメントは以下の通りです。
http://docs.aws.amazon.com/cognito/latest/developerguide/cognito-dg-pdf.pdf
bowerを使ってangularとbootstrapのウェブアプリ開発環境を秒速で整える方法
(注:mac前提で書いています。windowsのことは良くわからないです…)
急ぐ人のために
bowerとか既に入っているよ、という人は以下のコマンド(1,2)を打った後に、後ろの方についているHTML、js、cssファイルをコピーしてください。以上です。
- bower install angular-bootstrap
- bower install bootstrap
ファイル構成は、以下を想定しています。
ゆっくり解説
ここからは、秒速ではなく、1つ1つやっていきます。
流れとしては、homebrew → nodebrew → nodejsとnpm → bower → angularとbootstrapのインストールと言う流れになります。
homebrewについて
homebrewは、apple社が予め入れといてくれなかった、でも自分には必要!!、というソフトウェアのバージョン管理に使います。
今回のケースだと、nodebrewですね!
- インストール
以下のコマンドをコンソールに打ち込みます。(1行です)
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
上のコマンドは、変わっているかも知れないので、本家を確認することをオススメします。
brew.sh
nodebrew について
nodebrewは、nodejs周りのパッケージのバージョン管理ツールです。
nodejsで複数の環境を切り替えながら開発する時に便利です。
普段は、最新版を使いつつ、例えば、AWSのlambda向けの開発時には、バージョン、XX.xでと言うように使うと便利です。(多分)
- インストール
brew install nodebrew
もしくは、以下のgithubの手順でインストールします。(こちらだとhomebrew不要です。)
github.com
nodejsとnpmについて
nodejsは、サーバサイドのjavascript実行環境です。とは言いつつ便利なのでローカル環境でも使っています。
IOの非同期処理に特化しており、大量のIOを消費するような昨今のサーバにピッタリです(AWSはこれが無いと辛いです)。
npmは、nodejsで使えるライブラリ群を管理してくれます(npm install hogehogeでサクサク入るので便利です)。
- インストール
nodebrewを最新版にします。
nodebrew selfupdate
nodejsとnpmをインストールします。ここでは、バイナリでインストールしています。
(ホーム直下に、.nodebrew/srcがない場合は作成してください。)
nodebrew use latest
入ったバージョンを確認します。
nodebrew ls
バージョンを指定して使います(下のXは自分の環境に入ったバージョン番号にしてください)。
PATHを通して置かないと(bashだとexport PATH=$HOME/.nodebrew/current/bin:$PATH)して置かないと、nodeとnpmが見つからないので注意してください。
nodebrew use vX.XX.XX
もしくは、以下の本家からインストールします(この場合は、nodebrew不要です)。
Node.js
bowerについて
bowerとは、ウェブのためのパッケージ管理ソフトです。
要は、ブラウザ上でjavascriptを使ってウェブアプリを作成する際のパッケージ管理をしてくれます。
- インストール
以下のコマンドを実行します。
gオプションをつけているので、環境全体にインストールされます(嫌いな人は外しましょう)。
npm install -g bower
angularについて
angularhは、google社が提供しているウェブアプリ向けのjavascriptライブラリです。
利点をあげると以下のようになるでしょうか。
- DOMを直接操作しなくて良い。
- 流儀に則って書くだけで、ModelとViewとControllerに勝手に分かれるので、後から見てわかりやすい。
- Directiveを使うとHTMLの操作が楽
- 独自にインポート機構を持っているので、ファイルに分割する場合に気にすることが少ない。
- インストール
今回は、bootstrapと一緒に使うので、以下のコマンドを実行します。
bower install angular-bootstrap
サンプルファイル
- HTMLはコチラ (index.html)
<!DOCTYPE html> <html ng-app="app"> <head> <meta charset="utf-8" /> <link rel="stylesheet" type="text/css" href="bower_components/angular-bootstrap/ui-bootstrap-csp.css"/> <link rel="stylesheet" type="text/css" href="bower_components/bootstrap/dist/css/bootstrap.css"/> <link rel="stylesheet" type="text/css" href="css/app.css"/> <script type="text/javascript" src="bower_components/angular/angular.min.js" ></script> <script type="text/javascript" src="bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js"></script> <script type="text/javascript" src="js/app.js" ></script> </head> <body> <div class="container sample" ng-controller="ButtonsCtrl"> <div class="row"> <div class="col-lg-8"> <button type="button" class="btn btn-primary"> hello bootstrap (no action)</button> </div> <div class="col-lg-4"> <button type="button" class="btn btn-primary" ng-click="click_btn()"> hello angular</button> </div> </div> </div> </body> </html>
- jsファイルはこちら (js/app.js)
ui.bootstrapのライブラリを読み込んで使っています。
"app"と言うモジュールにしていますが、ここは適宜変更してください(変更した場合は、html側の修正もお忘れなく)。
"ButtonsCtrl"も同様です。
angular.module('app', ['ui.bootstrap']) .controller('ButtonsCtrl', ['$scope',function($scope) { $scope.click_btn = function() { alert("DONE!") } }]);
ぶっちゃけ無くても動きます。
div.sample { margin-top : 30px; }