Angularの環境構築と使い方

Angularとは、フロントのSPA (Single Page Application)のアプリ開発をする際に重宝するフレームワークでして、コンポーネント単位で開発でき、ルーティング機能もあるため、レスポンスの速い快適なアプリケーションが開発できます。(他にreactやVue.js等もあります。)

なぜ、Angularなどのフレームワークがレスポンスが速いのかは、以前に書いたreactのページで記載しました。基本的にはAngularも同じです。

ここでは、Angularのインストール・環境構築から、簡単なWebサイトの作成までをご説明します。

よくある、下記のようなレイアウト構成のページをコンポーネント単位で作成し、コンテンツをサイドメニューから切り替えることができるようにします。これだけでもレスポンスが速いのがわかると思います。

では早速、作っていきましょう。

Angularのインストールと環境構築

まず、Angular CLIをインストールするのですが、npmコマンドでインストールしていくため、もしnpmコマンドが無い場合は、下記のnodeのページからNode.jsをダウンロードしてインストールすれば、npmも入ります。

nodeのWebページ

まずは、Angular CLIをインストールします。

# npm install -g @angular/cli

これだけで基本的にはAngularのインストールは完了です。ですが、レイアウトや便利部品などを使うためにmaterialは必須になってきますし、フォームやhttp通信をする場合も別途モジュールを追加していく必要がありますので、ここでは、materialというモジュールを追加してみます。

まず、Webサイトのアプリケーションを作成します。(この中でモジュールを追加していくので)

sampleというアプリケーション名にします。

# ng new sample

これで、sampleというフォルダができたと思います。この中に入ります。

# cd sample

materialのモジュールを追加します。

# ng add @angular/material

そして、src/app/app.module.ts に、materialの中のMatGridListModuleというモジュールを使うことを書きます。

import { MatGridListModule } from '@angular/material/grid-list';

同ファイルのNgModuleの中にも追加します。

 imports: [
    BrowserModule,
    AppRoutingModule,
    MatGridListModule
  ],

これで準備完了です。

コンポーネントの作成

次に、ヘッダ、メニュー、コンテンツ1、コンテンツ2、フッターのコンポーネントを作っていきます。コンポーネントを作るときも、コマンドラインで作ります。

# ng generate component header
# ng generate component menu
# ng generate component content1
# ng generate component content2
# ng generate component footer

そして、src/app/app.component.htmlを下記のように書き換えます。最初に何かたくさん書いてありますが、全部消して大丈夫です。

<app-header></app-header>
  <mat-grid-list cols="4" rowHeight="50px">
  <mat-grid-tile colspan="1" rowspan="6">
    <app-menu></app-menu>
  </mat-grid-tile>
  <mat-grid-tile colspan="3" rowspan="6">
    <app-content1></app-content1>
  </mat-grid-tile>
</mat-grid-list>
<app-footer></app-footer>

まずは、コンテンツ1(content1)だけを表示します。何をやっているのかというと、全体を4つの列に分けて(cols=”4″)、menu部分を1列分(colspan=”1″)、content1部分を3列文(colspan=”3″)にして表示しています。

では、ここまでのソースコードをブラウザに表示して確認してみます。apacheやnginx等のWebサーバで動かすこともできるのですが、手っ取り早く確かめるには、違うポートでAngularのWebサーバを立ち上げて確認します。

# ng serve --port 8080

これで、localhostの8080番ポートでこのサイトが見れるようになりましたので、ブラウザで、http://localhost:8080/ にアクセスしてみましょう。

header works!や、menu works!、content1 works!、footer works!が表示されていればできています。

Routerでページ切り替え

では、次にメニュー部分にリンクを作成し、コンテンツ部をcontent1とcontent2で切り替えられるようにします。

まず、content1を表示させていた部分をrouterで切り替えられるようにします。

先ほどの src/app/app.component.htmlの中のcolspan=”3″の中を書き換えます。

<mat-grid-tile colspan="3" rowspan="6">
    <div class="main">
      <router-outlet></router-outlet>
    </div>
  </mat-grid-tile>

次に、menu部分のコンポーネントのhtmlを作成します。src/app/menu/menu.component.htmlに下のように書きます。

<ul>
  <li><a routerLink="/content1">コンテンツ1</a></li>
  <li><a routerLink="/content2">コンテンツ2</a></li>
</ul>

リンク部分が href ではなく、routerLink になっております。AngularのRouter機能を使ってページを切り替えるためです。ですが、このままだとRoutingされませんので、もう少し設定します。

src/app/app-routing.module.ts を下のように追記します。

import { Content1Component } from './content1/content1.component';
import { Content2Component } from './content2/content2.component';

Routesの変数にも追記します。

const routes: Routes = [
  { path: '', redirectTo: '/content', pathMatch: 'full' },
  { path: 'content1', component: Content1Component },
  { path: 'content2', component: Content2Component }
];

これでもう一度、ブラウザで確認してみましょう。

先ほどのng serveが立ち上がったままでしたら、すでにブラウザに自動で反映されていると思います。止めていたら、再度同じように立ち上げましょう。

メニュー部分に、「コンテンツ1」と「コンテンツ2」のリンクができていて、クリックすると、それぞれ、コンテンツ部が content1 works!とcontent2 works!に切り替えられたらできています。

ページのリロード無しで切り替えられるため、速いです。(コンテンツがこれだけだとあまりわからないかもしれませんが)

Webサーバで見れるようにする

ここまでは、ng serveコマンドで8080番ポートに簡易なWebサーバを立ち上げて確認していました。ですが、実際にデプロイするときには、apacheやnginxなどの既存のWebサーバのどこかのフォルダに置きたいと思います。そのための作法についても説明します。

まず、Webサーバにデプロイするためには、ソースコードをビルドする必要があります。この時に、この作成したWebページを、Webサーバのどのフォルダに置くかを決める必要があります。ドキュメントルートからの相対的なパスで指定します。

例えばローカルホストのサーバで、

http://localhost/sample/

という場所で見れるようにするには、相対パスが /sample/ ということをビルド時に指定します。

# ng build --base-href=/sample/

これでビルドされて、ワークスペースの dist/ というフォルダに sampleというフォルダができています。これをこのまま、Webサーバのドキュメントルートにコピーすれば、上記のURLで見れるようになります。

apacheの場合は、/var/www/html がドキュメントルートの可能性が高いです。

SageMakerで独自コンテナを使って自動学習

機械学習の学習ってGPUが必須だと思いますが、個人や会社でGPUマシンが無い場合は、クラウドのGPUインスタンス等を使う必要があります。GPU使っても2〜3日かかる学習などもあると思いますので、結構GPUインスタンス代が馬鹿になりません。立ち上げてるだけで料金が発生しますので、学習終わったら、一刻も早くインスタンスを終了させたいです。

Lambda等で自分で作成して、GPUインスタンスの起動、学習、停止まで一連のプロセスを自動化することもできますが、そういうのをやってくれるSageMakerというAWSのサービスがありますので、SageMakerを紹介します。

SageMakerとは

SageMakerとは、データさえS3に用意すれば、機械学習を誰でも簡単にできるようなサービスです。TensorflowやMXnetなどのコンテナも用意されており、それらを使うなら環境構築など面倒なことを一切やらず、起動、データ入力、学習、モデル出力、停止まで簡単に実現できます。

ですが、やっぱり既存のコンテナには無い機能やライブラリを使っていたり、フレームワーク自体を改造していたりすることを往々にしてあると思います。ここでは、独自のコンテナを使ってSageMakerを利用し、一連の処理を実行する例を紹介します。

独自コンテナを使う場合の利用方法

まず全体の流れですが、下の図のような感じになります。

前準備としては、学習用のソースコードも含めたコンテナをECR(Elastic Container Repository)にアップロードしておく必要があります。SageMakerからロードして使います。

学習時にユーザ(開発者)がやることとしては、

  1. 学習データをS3にアップロードする。
  2. SageMakerのJupyterノートブックで、学習スクリプトを実行する。

これだけです。これで、自分のコンテナがロードされ、その中で学習データが学習され、モデルがS3に出力されます。

学習の起動は、SageMakerのJupyterノートブックインスタンスで行うのですが、ここに、ロードするコンテナのURIや、学習データのS3 URI、出力モデルのS3 URIを記載して fit()すると全部自動でやってくれます。

独自コンテナの作り方

では、コンテナはどのように作っておけばいいかなのですが、少しだけSageMaker的ルールがあります。

ルール1

学習時には、docker run image trainコマンドが実行される。つまりコンテナ内で trainコマンドを自作し、実行権限をつけてPATHを通しておかなくてはいけない。

ルール2

学習データは S3から、コンテナ内の /opt/ml/input/data/チャネル名 にダウンロードされて上記 trainコマンドが実行される。つまり、このパスにあるデータを学習するように作っておく必要がある。

ルール3

学習終了時には、/opt/ml/model 以下のファイルをS3にアップロードする。つまり、生成したモデルをここに置く必要がある。

以上のルールを考慮してコンテナを作ります。ルール1のtrainコマンドのPATHですが、/opt/program/train に置くのが良いのですが、それでもPATHを設定していないとエラーになります。なので、コンテナをbuildする時のDockerfileで設定します。

Dockerfileの例

FROM ベースとなるイメージ
SHELL ["/bin/bash", "-c"]
WORKDIR /opt/program
ENV PATH="/opt/program:${PATH}"

このようなDockerfileを作ってPATHに /opt/programを通します。そして、

# docker build -t 生成するイメージ名 .

buildコマンドでイメージを作成します。上記のDockerfileのあるフォルダで実行します。そして、このイメージをECRにpushすればOKです。

Jupyterノートブックからの起動方法

SageMakerで学習を行うために、Jupyterノートブックインスタンスを使います。ここで学習用のコンテナを起動させるのですが、Jupyterノートブックインスタンスとは別のインスタンスとして立ち上げるため、ノートブックインスタンスはGPUインスタンスではなく、ml.t2.mediumなどで良いかと思います。

学習用のノートブックのスクリプトの概要です。

from sagemaker import estimator
from sagemaker import get_execution_role

bucket='S3のバケット名'
image_uri='ECRのイメージURI'

role = get_execution_role()

data_location = 's3://{}/input'.format(bucket)
output_location = 's3://{}/model'.format(bucket)

estimator = estimator.Estimator(
    image_uri=image_uri,
    role=role,
    train_instance_type="GPUインスタンスのタイプ(ml.p2.xlargeなど)",
    output_path=output_location
)

estimator.fit({チャネル名: data_location})

このようなスクリプトを書いて実行するだけで、/opt/ml/input/チャネル名にデータがコピーされ、あとは自動で学習をやってくれます。できたモデルはS3のバケットにアップロードされます。

SageMakerの使い所

ここまでの作業を自分ひとりでできて、自前でGPUも持っている方は、SageMakerを使う必要はほぼ無いと思います。ですが、誰か別の人や、他社の方に学習環境を提供する時などには、SageMakerはとても便利です。もちろん、自前でGPUを持っていない場合は、自分で使うのにも良いと思います。

ReactをApacheで動かす

ブラウザのアプリ開発もSPA (Single Page Application)が主流となりつつあります。Slackとかブラウザで動作させていても快適ですね。

このSPAのアプリを開発するJavascriptのフレームワークとして、Angular、React、Vue.js等があります。今回は、このReactをApacheで動かすまでのお話です。

Reactとは

まずReact等のSPA用のライブラリがなぜ便利なのかですが、基本的にはコンポーネントを分離して開発しやすいのが大きなメリットだと思います。まず前提としてはページに何か更新があった時には、ページ全体を更新(DOMの再構築)するのではなく、更新した部分だけを変更します。

ページはヘッダ部やサイド部などいろいろなコンポーネントに分かれていると思いますが、例えばサイド部のボタンを押して何か更新したときに、ヘッダ部やコンテンツ部にも変更がおよぶことがあると思います。これがとても面倒なことだということは何となくわかるかと思います。

Reactライブラリを使った場合、ボタンを押した結果を再描画する場合は、Reactライブラリに「全体再描画」という依頼をするだけで、差分のある部分だけを更新してくれます。これにより、各コンポーネントを独立して開発しやすくなります。

前準備

では、実際にReactを使うための説明に入ります。Reactの開発をするためには、node.jsやnpmコマンドが必要になってきますので、これらをインストールします。

Linuxの場合 (Ubuntu)

# apt install nodejs npm

Mac OSの場合 (まずnodebrewというnpmやnode.jsを管理するプログラムを入れます)

# brew install nodebrew
# nodebrew install-binary latest

でいけるかと思います。

Create ReactAppのインストール

Create ReactAppは、新しいReactアプリを作成するのに便利なツールで、Reactを学習するための快適な環境があります。まず、これをインストールします。

# npm install -g create-react-app

Reactのサンプルアプリの作成

ReactのHelloWorld的なサンプルアプリは、上記でインストールしたCreate ReactAppを使って簡単に作成できます。

# npx create-react-app react-sample

これで、カレントディレクトリにreact-sampleをというフォルダができて、その下に初期プログラムができています。試しにこれが動くかどうかを確認してみましょう。react-sampleというフォルダに移動して、下記のコマンドを実行します。

# npm run start

3000番ポートでWebサーバが立ち上がり、Reactのアプリが閲覧できるようになります。自動でブラウザが立ち上がると思いますが、http://localhost:3000/ にアクセスすると閲覧できると思います。

こんなページがでたら成功です。

Apacheから閲覧できるようにする

先ほどのテストは独自のWebサーバを3000番ポートで立ち上げて確認できましたが、実際にreactアプリを運用するにあたっては、HTTP(80番ポート)でアクセスできるようにしたいです。ですが大抵の場合WebサーバはApacheやnginxがWebサーバとして動いているので、reactだけの専用サーバを80番ポートで立ち上げられない場合が多いです。

ここでは、Apacheで上記のreactアプリを動かす手順を書きます。

まず、先ほどのreactアプリをbuildする必要があるのですが、デプロイするURLを設定する必要があります。これは、package.jsonという設定ファイルに追記します。

{
  "name": "react-sample",
  "version": "0.1.0",

このような記載で始まっていると思いますが、nameの下にでもデプロイするURLを下記のように追記します。

{
  "name": "react-sample",
  "homepage": "http://localhost/react-sample/",
  "version": "0.1.0",

そして、buildします。

# npm run build

そうすると、上記のURLにデプロイする用にbuildされ、buildフォルダが作成されます。そのbuildフォルダを Apacheのドキュメントルートから参照できる場所にコピーすれば Apacheから閲覧できようになります。Apacheのドキュメントルートが /usr/local/htdocs の場合は、

# cp -R build /usr/local/htdocs/react-sample

これで、http://localhost/react-sample のURLで閲覧できるようになります。

Hello World的なものを作ってみる

ここまでだと、何のファイルがどういう役割で表示されているのかわからないです。src/ の下にあるファイルを編集して、buildし、buildフォルダをコピーすれば変更が反映されますが、どこを編集すれば良いかを説明します。

まず、index.jsからスタートします。

ReactDOM.render(
  <react.strictmode>
    <app>
  </app></react.strictmode>,
  document.getElementById('root')
);

このようなスクリプトが書いてあると思います。これはこのままで良いのですが、ここで <App />というタグがあり、Appが実行されます。Appは App.jsというファイルに書かれていまして、それをimportしております。

App.jsを見ると、下記のようなスクリプトがあり、divで囲まれた中が描画されます。サンプルは画像を表示するものでしたが、ここを書き換えてHello Wolrdを表示してみます。

function App(){
    return (
        <div classname="App">
           <h1> Hello World </h1>
           ここに書いたものが描画されます。
        </div>
    );
}

divタグの中にHTML的なものを書いていけば、それが表示されます。これだと普通にHTMLを書くのと変わりないので、RouterやBrowserRouterという機能を使って、コンポーネントを組み合わせてページを作るところまで説明します。

まず、react-router-domというものをインストールする必要があると思います。

# npm install react-router-dom

これでインストールできると思います。そして、ページをHeader部、Content部、Footer部にわけて表示してみます。 App.jsを下記のように書いて、Header, Content, Footerを読み込むようにします。各コンポーネントは、Header.js, Content.js, Footer.js に記載します。

import { Route ,BrowserRouter} from 'react-router-dom';
import Header from './Header';
import Content from './Content';
import Footer from './Footer';

function App(){
    return (
        <div classname="App">
         <h1>Hello World</h1>
         <browserrouter>
           <route path="/" component="{Header}/">
           <route path="/" component="{Content}/">
           <route path="/" component="{Footer}/">
         </browserrouter>
        </div>
    );
}
export default App;

Header.js、Content.js、Footer.jsは下記のように書いておきます。(Header.jsだけの例)

import React, { Component } from 'react';

class header extends Component{
    render(){
        return(
            <div>
              ここはヘッダ部です。
            </div>
        );
    } 
}
export default header;

こんな感じでContent.jsもFooter.jsも書いて buildすると、各コンポーネントを読み込んだHTMLを描画してくれます。

SPAを作るためにRouter機能は必要となってくるので、ここまでイメージができていると Reactに入っていきやすくなります。

M系列符号で自己同期

人類の全ての活動がリモートで可能かどうか試されている今、リモートで再生される音声を同期させたいということもあるかと思います。リモート演奏等。

そこでM系列符号で自己同期を取る方法を思い出しましたので、簡単にご紹介します。

M系列符号とは

M系列符号とは、nビットのシフトレジスタから生成される周期が2^n-1の符号列なのですが、とても便利な特徴があります。

  • 自己相関のピークが1周期に1回(ぴったり合った時)だけあり、それ以外は-1(相関ほぼ無し)になる。
  • 0と1の発生確率がほぼ等しい。(擬似ノイズとしても使える)

この自己相関の特徴が、同期を取るのに大変便利なので、同期処理や測位など幅広く使われています。

自己相関のイメージ図を書きます。3ビットのシフトレジスタから生成された7ビットのM系列符号の例です。

7ビットのM系列符号を1ビットずつずらし、各ビットをXOR計算して加算したものが自己相関です。(XORで0の場合に-1を加算します。)上の図のようにぴったり合ってる場所だけが自己相関値が7になり、その他は-1(3勝4敗)になります。ほぼ無相関です。これを使えば、どこが始まりかわからない信号でも先頭を検出できます。

M系列符号生成のサンプルプログラム

M系列符号は、生成多項式から構成されるシフトレジスタの出力で作れるのですが、下記のようなプログラムで生成できます。生成多項式x^8 + x^4 + x^3 + x^2 + 1で生成する255ビットのM系列符号の例です。

#define SpreadingRate  (255)  
#define Generator    (11101)
#define MaxBit       (0x0100)
#define XorBit       (0x011d)

char * code_generator()
{
  int i;
  char *code;
  unsigned int shift = Generator;

  code = (char *)calloc(SpreadingRate, sizeof(char));

  for(i = 0; i < SpreadingRate; i++){
    if(shift & 0x01){
      code[i] = 1;
    }else{
      code[i] = 0;
    }
    shift = shift << 1;
    if(shift & MaxBit){
      shift = shift ^ XorBit;
    }
  }

  return(code);

}

SpreadingRateは符号ビット長になり、生成多項式の次元数nの2^n-1になります。Generatorは、シフトレジスタの初期値なので何でも良いです。変えると符号ビット列が巡回します。MaxBitは最高次のみ1とするビット列で、XorBitは最高次を含めた各係数のビット列です。

M系列符号の自己同期で遊んでみました

M系列符号を音声信号にしてスピーカから送信し、マイクで録音して自己同期をとるという遊びをしてみました。図に書きます。

encoderは、M系列符号を音響信号にしてWaveファイルを生成します。このWaveファイルをスピーカで再生し、マイクで録音してWaveファイルにします。この録音Waveファイルをdecoderで自己相関計算し、ピーク(同期ポイント)が検出できるかをやってみました。

こちらが1サンプルずつずらして自己相関を計算した相関値です。あるポイントだけ鋭いピークが立っているので、ここがM系列符号の始まりだということがわかります。反射波などがあれば、他の場所にも少しピークが立ちます。つまり音響経路のインパルス応答と見なせます。

ちなみにM系列符号は、下図の左側のようにそのまま矩形波で送っても良いですし、右側のように位相変調して送っても同じ信号で相関を取ればよいのでどちらでも可能です。

この遊びのサンプルプログラムをこちらにおきますので、何かお役に立てそうでしたらお使いください。

Msequence-sample.zip

Code 3兄弟で自動デプロイ

AWSのCode 3兄弟とは、CodeCommit、CodeDeploy、CodePipelineの3つのセットで、これらを使うと、ソースコードのgit管理、デプロイを結び付けて自動デプロイが可能になります。

おおまかなイメージ図を書きました。

Code Commitを使ってソースコードをgit管理していれば、それと連携してEC2インスタンスに自動でデプロイすることが可能です。

設定方法は、こちらの公式チュートリアル(シンプルなパイプラインを作成する)がとても参考になります。

イメージだけ説明すると、まずEC2インスタンスにCodeDeployエージェントというものをインストールしておく必要があります。

次にCodeDeployで、どのEC2インスタンスにデプロイするか等を設定します。デプロイ先のどこに配置するかは、ソースコードの中の トップフォルダのappspec.ymlファイルに書きます。上図の例だと、各EC2インスタンスの /var/source に配置されるようになります。EC2インスタンスは、デプロイグループの設定のタグで関連付けすることで設定できます。

CodePipelineでは、CodeCommitにgit push(ソースコードのアップデート)があったことをトリガーとして動作するように設定します。このトリガーに基づいてCodeDeployアプリケーションを実行します。

ソースコードの中のappspec.ymlには、CodeDeployでEC2インスタンスにインストールする前や後に実行するスクリプトも設定できます。したがって、インストール前にサーバプロセスを停止したり、インストール後にサーバプロセスを再起動する等、詳細な設定が可能です。

これで、開発者はソースコードの開発に集中できるようになりますね。本当に便利な3兄弟です。

MeCabのインストール

日本語の自然言語処理をする場合、文章を単語に分割したり、形態素解析をしたりする必要があります。そのツールがMeCabです。pythonならspacyというライブラリでもできますが、ここではMeCabのインストール、使い方を説明します。

MeCabのインストールですが、パッケージで入れることもできますが、環境によっては文字コードの設定ではまる可能性があるため、ソースからインストールすることをおすすめします。

MeCabのソースコードは、https://github.com/PROoshio/mecab にありまして、gitでdownloadできます。

# git clone https://github.com/PROoshio/mecab.git

ここにMeCabのプログラムと辞書が両方入っています。中のREADME.mdを読めばやることはわかりますが、一応書きます。まずはプログラムの方です。

# tar zxvf mecab-0.996.tar.gz
# cd mecab-0.996/  
# ./configure --with-charset=utf8
# make
# make check
# sudo make install

続いて辞書の方です。

# tar zxvf mecab-ipadic-2.7.0-20070801.tar.gz
# cd mecab-ipadic-2.7.0-20070801/  
# ./configure --with-charset=utf8
# make
# sudo make install

これで完了です。./configureのところで、–with-charset=utf8をつけるのを忘れないでください。これで明示的に文字コードをUTF-8にすると宣言しています。

辞書の方のmakeで 「libmecab.so.2がない」等のエラーがでる場合は、環境変数のLD_LIBRARY_PATHにlibmecab.soのライブラリのパスを追加すると makeできます。デフォルトだと /usr/local/bin です。cshやtcshの場合は、

# setenv LD_LIBRARY_PATH /usr/local/lib:${LD_LIBRARY_PATH}

などで、LD_LIBRARY_PATHにパスを追加できます。

mecabコマンドができているので、mecab -D で文字コードを確認してみましょう。

# mecab -D
filename:	/var/lib/mecab/dic/debian/sys.dic
version:	102
charset:	utf-8
type:	0
size:	751185
left size:	1876
right size:	1876

ここのcharset: が utf-8になっていれば大丈夫です。Shift-JISとかEUCとかになっていると後々困る可能性があります。では、何かコマンド上で入力してみましょう。mecabコマンドを実行した後、日本語を入力してEnterを押せば形態素解析の結果がでてきます。

# mecab
今日は雨です。
今日 名詞,時相名詞,*,*,今日,きょう,代表表記:今日/きょう カテゴリ:時間
は	助詞,副助詞,*,*,は,は,*
雨	名詞,普通名詞,*,*,雨,あめ,代表表記:雨/あめ 漢字読み:訓 カテゴリ:抽象物
です 判定詞,*,判定詞,デス列基本形,だ,です,*
。 特殊,句点,*,*,。,。,*
EOS

「今日は雨です。」と入力した時の例です。

次に、pythonからMeCabを使えるようにします。mecab-pythonというライブラリを pipでインストールします。

# pip install mecab-python
これでpythonからもMeCabをimportすれば、形態素解析や分かち書きができます。 形態素解析の使い方は、MeCab.Tagger(‘-o chasen’) で 形態素解析器を作れば良いです。
import MeCab
mecab = MeCab.Tagger('-o chasen')
text = "今日は雨です。"
result = mecab.parse(text)
print(result)

次に分かち書きの例ですが、MeCab.Tagger(‘-O wakati’)にするだけです。

import MeCab
mecab = MeCab.Tagger('-O wakati')
text = "今日は雨です。"
result = mecab.parse(text)
print(result)

分かち書きの方は、単語をスペースで区切った文字列として出力されます。split()関数などを使えば単語分割したリストを作れます。

NEologdのインストール

上のmecabの辞書も結構、標準的な固有名詞はちゃんと判別してくれますが、最近話題になった単語(固有名詞)はさすがに無理です。そこでWeb上から得た新語に対応したシステム辞書がNEologdです。「コロナ禍」とかちゃんとひとつの固有名詞として取得してくれます。

NEologdのインストールは、https://github.com/neologd/mecab-ipadic-neologd からダウンロードしてできます。

# git clone https://github.com/neologd/mecab-ipadic-neologd.git
# cd mecab-ipadic-neologd
# ./bin/install-mecab-ipadic-neologd

最後に辞書がインストールされたパスが表示されますので、そのパスをmecabの-dオプションで指定すれば、NEologdの辞書で形態素解析できます。

普通のmecab辞書だと、「コロナ禍で大変です。」が

# mecab
コロナ禍で大変です。
コロナ	名詞,一般,*,*,*,*,コロナ,コロナ,コロナ
禍	名詞,接尾,一般,*,*,*,禍,カ,カ
で	助詞,格助詞,一般,*,*,*,で,デ,デ
大変	名詞,形容動詞語幹,*,*,*,*,大変,タイヘン,タイヘン
です	助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
。	記号,句点,*,*,*,*,。,。,。
EOS

「コロナ」と「禍」で切られてしまいますが、NEologdだと

# mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd
コロナ禍	名詞,固有名詞,一般,*,*,*,コロナ禍,コロナカ,コロナカ
で	助詞,格助詞,一般,*,*,*,で,デ,デ
大変	名詞,形容動詞語幹,*,*,*,*,大変,タイヘン,タイヘン
です	助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
。	記号,句点,*,*,*,*,。,。,。
EOS

と、「コロナ禍」というひとつの固有名詞として取得できました。

最近のワードを扱う場合には、NEologdの方が良いかもしれないですね。

マイクロサービス開発

 マイクロサービスとは近年注目されている開発手法で、小さな単位のサービスを違いに連携させてソフトウェアを開発・運用・管理する考え方です。

2014年にマーチン・ファウラー氏らによって書かれた記事で知られるようになりました。

全ての機能は疎結合であるべきという哲学のもと、個々の機能はAPIで実装し、サービスを提供するソフトウェアとは分離して開発・運用・管理できることがメリットです。そして複数人で開発する際にも、機能ごとに明確に分離されていると分担もしやすいです。

従来のモノリシックサービス開発と比較した図を記載します。

モノリシックサービス開発では、個々の機能は関数やライブラリ等で分かれているとはいえ、ソフトウェアの中に構成され、1つのプロセスとして動作します。それに対して、マイクロサービス開発では、個々の機能はAPIという形で実装され、それぞれ別のプロセスとして動作します。

APIはRESTという設計原則で構築されます。RESTとは、簡単にいえばAPIがステートを持たないことです。1リクエストに対して1レスポンスで完結するものです。

ですので、APIは本当に最小単位の機能に落とし込んで設計する必要があります。機能の分割という意味では、UNIXコマンドが良い例でして、シンプルな機能のコマンドがたくさんあります。各コマンドをパイプ(前のコマンドの標準出力と次のコマンドの標準入力にする)でつなげることで複雑な技も実現できるようになります。

このように、APIもシンプルな最小単位の機能に分割しておき、ソフトウェア側でいろいろ組み合わせて複雑な機能を実装していく手法がマイクロサービス開発という開発手法になります。

運用や管理の面から考えても、何か1機能をアップデートする度に、モノリシックサービスだと、ソフトウェアをアップデートする必要があるかもしれませんが、マイクロサービスだと、その機能のAPIだけをアップデートすれば良いので運用・管理も楽になります。

APIごとにコンテナ化しておけば、実行環境の依存性も他のAPIに影響を与えないため、機能拡張などもしやすいです。

このように、マイクロサービス開発の哲学を実践することでスケーラビリティが格段に良くなりますが、あまりに分離しすぎると、メモリやハードウェアリソースが無駄に使われてしまうこともあるため、バランス良く分離するのが良いのかと思います。

量子コンピュータで遊んでみました

 量子コンピュータが徐々に実用化が見えてきています。とは言っても私たちの家庭のPCが量子コンピュータになることはありません。特殊で量子コンピュータが得意な計算をサーバに送って、結果をもらうという使い方になります。なので、現在、私たちが使っている古典コンピュータがなくなることはありません。

 量子コンピュータをできるだけ簡単に説明して、qiskitというpythonのライブラリで遊んでみるところまで解説してみたいと思います。

量子ビットの仕組み

量子コンピュータのビットは、私たちが使っている古典コンピュータのビットとはかなり違う仕組みをしています。古典コンピュータのビットは、ある素子の電圧がONかOFFかで、そのビットの値が0か1かを決めています。量子コンピュータのビットも、ある量子の状態を観測してビットの0と1を判定するのですが、この量子は観測するまで状態が確定しません。0か1かどっちかわからない状態で計算をして最後に観測したら結果だけわかるという使い方になります。

ただ、量子ビットを操作することによって、0の確率を高くしたり、1の確率を高くすることはできます。量子ビットを理解する上で大事なブロッホ球の図を書きます。

図1 ブロッホ球

左側は、XYZ軸を持つ空間上に量子の状態を表す球面を描き、その球面上に量子の状態があるとしています。ビットの値を決めるのはZ軸です。Z軸上のてっぺんがビット0に、反対側の真下がビット 1になる状態です。その他の球面上の点は、Z軸に射影して上の方であれば0の確率が高い、下の方であれば1の確率が高くなりますが、上で記述したように観測するまでどっちになるかわわかりません。赤線の状態ベクトルの例だと、70%ぐらいの確率で0、30%の確率で1という状態になります。

このブロッホ球は、数学的に解釈するためのもので、実際には電子のスピンの向きを使っていたりしますが、総じて観測するまでわからないものを使っています。

このわからないまま計算を実行できることが、並列に計算できる仕組みでもあり、量子コンピュータの独特な特徴でもあります。計算とは何かというと、このブロッホ球的に言うと、ある量子の状態をX軸を中心に回転させたり、Y軸中心に回転させたり、はたまた任意の軸を作ってその軸で回転させたりと、それによって状態が変わり、0の確率、1の確率も変わっていきます。ある量子の状態に基づいて別の量子を操作することもできます。

量子回路

では、この量子ビットを使って、簡単なビットの足し算を演算する量子回路を書いてみます。

量子回路

量子ビットを4つ使います。量子ビット1と2は入力ビットで、量子ビット3と4は出力ビットとします。

全ての量子ビットは0で初期化されているとします。まず、量子ビット1と2に「H」というアダマールゲートを使い、0が50%、1が50%という状態にします。つまり完全ランダムなビットとなります。

次にCNOTゲート(制御ビットが1の時だけ標的ビットを反転させる)を使い、量子ビット3に足し算結果の下位ビットをだすようにします。そして、量子ビット4にトフォリゲート(制御ビット2つが両方1の時だけ標的ビットを反転させる)を使うことで、足し算の繰り上がりビットをだすようにします。

最後に量子ビット3と4を「M」で観測しています。観測した時点で量子ビット3と4のビットが決まります。入力は「H」でランダムになっているので結果は観測するまでわかりませんが、00か01か10になるはずです。いわゆる全ての入力パターンを同時に計算しているイメージです。これが量子コンピュータの特徴です。結果はやる度に変わりますが、その確率分布を見るとどの結果が一番あり得るのか等がわかってきます。

qiskitで量子回路を組んでみる

qiskitはIBM Researchによって開発された量子コンピュータ用のフレームワークで、python用のライブラリもあります。まずは、pythonのqiskitをインストールします。

# pip install qiskit

次に、上で説明した足し算の量子回路をpythonコードで書きます。

from qiskit import *

def main():

    # シミュレータの初期化
    simulator = Aer.get_backend('qasm_simulator')
    # 量子回路の作成
    circuit = QuantumCircuit(4,2)
    # 0ビット目と1ビット目をアダマールゲートで、0が50%、1が50%の状態にする
    circuit.h(0)
    circuit.h(1)
    # CNOTゲート
    circuit.cx(0,2)
    circuit.cx(1,2)
    # トフォリゲート
    circuit.ccx(0,1,3)
    # 2ビット目、3ビット目を観測する
    circuit.measure([2,3], range(2))
    # 回路の表示
    print(circuit)
    # 実行
    job = execute(circuit, simulator, shots=1000)
    result = job.result()
    count = result.get_counts(circuit)
    # 結果表示
    print(count)

if __name__ == '__main__':
    main()

このスクリプトを実行すれば、シミュレータ相手ですが、作成した量子回路の演算をしてくれます。1000回実行した時の結果の分布を表示するようにしています。

        ┌───┐                     
q_0: |0>┤ H ├──■─────────■────────
        ├───┤  │         │        
q_1: |0>┤ H ├──┼────■────■────────
        └───┘┌─┴─┐┌─┴─┐  │  ┌─┐   
q_2: |0>─────┤ X ├┤ X ├──┼──┤M├───
             └───┘└───┘┌─┴─┐└╥┘┌─┐
q_3: |0>───────────────┤ X ├─╫─┤M├
                       └───┘ ║ └╥┘
 c_0: 0 ═════════════════════╩══╬═
                                ║ 
 c_1: 0 ════════════════════════╩═
                                  
{'10': 248, '01': 513, '00': 239}
コンソール画面にこのように表示されました。最後の行が結果のカウント数です。 10が248回、01が513回、00が239回です。 01は、1+0でも0+1でも01になるため、2倍の確率になっています。ランダムに2ビット選んだ足し算の結果の確率分布に合ってますね。

量子コンピュータの応用

足し算だけでは、量子コンピュータのありがたみが全くわからないと思いますが、いろいろな状態の計算を並列にできることが、複雑な問題ではありがたい場合があります。

例えば、データベース検索の例だと、膨大なデータの中からあるデータを見つけるためには、O(n)の計算量がかかると思われますが、グローバーの探索アルゴリズムというのを使うと、量子コンピュータでO(√n)で探索できると言われています。

また、現在の暗号技術のベースとなっている素因数分解の問題についてもショアのアルゴリズムを使うと高速に解けるというものです。量子ビットがかなり必要なのでまだ現実的に暗号解読はできないです。

今後、量子コンピュータのハードウェア技術が進歩することによって、このような複雑な問題をAPIでサーバの量子コンピュータに投げて短時間で結果をもらうという世界がはじまりそうです。

Dockerでサーバ開発

 最近、サーバ開発となると当たり前のようにコンテナ技術(Docker)を使うようになってきたため、Dockerでサーバ開発する方法を書いておこうと思います。

まず、Dockerとは何かというと、仮想環境ではあるのですが、かつてのVMWareのようなOSの上に仮想OSが動くようなもととは違い、 カーネルをnamespacesで分離して、ユーザ、ファイルシステム、プロセス管理等を別にして新たな環境を構築するようなイメージです。 OSが二重に動くわけではないので、無駄なオーバーヘッドが無く仮想環境が実現できるということです。

これによって、ソースコードを実行環境とともにコンテナでお渡しできるのが一番のメリットだと思います。

Dockerインストール方法 (MacOS)

まず、MacにDockerをインストールするには、下記URLからダウンロードしてDockerアプリケーションをインストールします。

https://hub.docker.com/editions/community/docker-ce-desktop-mac

インストールができたら、MacのアプリケーションフォルダにDockerというアプリケーションがあるはずなので、これを起動します。上のメニューバーに くじらのようなDockerアイコンがでてれば起動中です。

次に、dockerhubからイメージを持ってくるのですが、イメージのpullだけならアカウントが無くてもできます。例えば、下記のコマンドで、docerhubからubuntu18.04のイメージを持って来れます。

# docker pull ubuntu:20.04

これでイメージがローカルのDockerにダウンロードされます。docker imagesコマンドで確認できます。そしてこのイメージからdocker runコマンドで コンテナを立ち上げます。

# docker run -it -d --name container_name ubuntu:20.04

container_nameのところは、コンテナの名前なので何でも好きなもので大丈夫です。docker psコマンドで見れると思います。 コンテナを作成したら、そのコンテナに入ります。

# docker exec -it container_name /bin/bash

これでコンテナの中に入れるので、あとはubuntu環境で遊べます。ただ、本当にOS以外何も入ってない状態から始まります。

Dockerコンテナ内の環境設定

コンテナ内がubuntuの例で書きます。

$ apt-get update

まず、これする必要あります。これでいろいろ必要なものをインストールできるようになります。

あと、gccとかmakeとかも入ってないのでこのあたりの開発コマンドを入れます。

$ apt update
$ apt install build-essential

pythonも使うなら、

$ apt-get install python3-distutils
$ apt-get install python3-dev

pipのインストールは

$ apt-get install wget
$ wget https://bootstrap.pypa.io/get-pip.py
$ python3 get-pip.py

ここまでやれば、AWSでubuntuのEC2インスタンスを立ち上げたぐらいの状態になってると思います。

ただひとつ困ったことがありました。dockerのシェル上で、Ctrl-pが使えないのです。(dockerのdetachのコマンドのため) Ctrl-pは、ひとつ前のコマンドを実行したいときに多用するので、これが使えないと困ります。一応2回押せばできるんですが、そんなのは無理です。 上矢印キーでもできるんですが、手をホームポジションから動かすのは嫌です。

ですので、Ctrl-pを使えるようにします。

~/.docker/config.json の設定ファイルの中に

"detachKeys": "ctrl-\\"  

と書いて、detach key をCtrl-pとは違うキーに割り当てます。これで、Ctrl-pが使えるようになります。

これでubuntuで遊べるようになりました。せっかくなので、この状態をイメージに保存しておきたいと思います。イメージとして保存しておけば、この状態からコンテナを作成できるようになります。

# docker commit [container_name] [image_name]:[tag_name]

これで、このコンテナが、[image_name]で保存されて、[tag_name]というタグが付いています。

docker-compose

 では、本題に入りますが、このDockerを使ってサーバ開発する場合に便利なdocker-composeについて書こうと思います。

まず、サーバ開発といえば、外部のネットワークから通信できる必要があるのですが、それには、ホスト側のポートと、コンテナ内のポートを 結びつけるポートフォワーディングの設定が必要になります。

これは、ホスト端末の〇〇番のポートにきた通信は、全てコンテナ内の〇〇番ポートに転送するという設定です。これをやらないと Dockerの中でサーバを立てても外からは使えないです。

あと、ファイルシステムもホスト側と共有できた方が良いです。ソースコードとかコンテナ内で開発するのでも良いのですが、 ホスト側でも編集した場合もありますし、機械学習のモデルファイルとか大きいので、こういうのはコンテナの外で共有した方が コンテナのイメージサイズも大きくならなくて済みます。

Dockerではホスト側のファイルシステムをコンテナ内にマウントすることができるので、これが便利です。

そしてこれらの設定を簡単にできるのが、docker-composeというツールです。dockerコマンドでひとつずつ設定していくのでも良いのですが、 毎回、設定するのは面倒ですので、設定ファイルに書いてdocker-compose一発でOKという状態にしたいと思います。

まず、docker-composeをインストールします。

# sudo curl -L https://github.com/docker/compose/releases/download/1.28.5/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

実行権限をつけます。

# sudo chmod +x /usr/local/bin/docker-compose

どこかのフォルダにdocker-compose.ymlというファイルを作り、以下の設定を書きます。

version: '2.3'
services:
  [service_name]:
    image: [image_name]:[tag_name]
    container_name: [container_name]
    tty: true
    ports:
      - 80:80
      - 443:443
    volumes:
    - /var/host:/var/docker

[service_name]は好きな名前で。[image_name]と[tag_name]は起動させるコンテナのイメージとタグ名です。 [container_name]は起動後のコンテナの名前です。tty trueは入れないとすぐ終了してしまうので入れましょう。

ports: のところに、ポートフォワーディングの設定を書きます。1行にひとつのポートずつ書きます。 コロンで区切ってますが、前方がホスト側のポート番号、後方がコンテナ側のポート番号になります。

volumes: のところに、ファイルシステムのマウントの設定を書きます。こちらのコロンの前方がホスト側のファイルパス、 後方がコンテナ側のファイルパスになります。上の例ですと、/var/host がコンテナ内の /var/docker にマウントされます。 (MacOSだと設定しないと/Usersか/tmpの下しかマウントできないかもしれません。)

設定ファイルができたら、その設定ファイルがある場所で、以下のコマンドを実行していきます。

# docker compose up -d

これで、先ほどの設定ファイル通りにコンテナが起動されます。-dをつけないとフォアグラウンドで動いてしまいます。

# docker compose ps

これで、docker composeで動いてるコンテナが確認できます。

# docker compose down

これで、docker composeで起動したコンテナを終了できます。

# docker exec [container_name] [command]

これで、docker内のコマンドを実行できます。

ホスト起動時にコンテナを立ち上げたい場合は、docker-compose.ymlの [service_name]の中に restart: always と書けば起動してくれます。コンテナの中のサーバは、上記の docker execでホスト側の起動スクリプトか、cronで 起動すればコンテナ内のサーバまで自動起動できます。

このようにやると、プログラムのソースコードもモデルファイルもホスト側で編集できるので、Dockerコンテナはプログラムの実行環境としてしか使っていないことになります。

自分のローカルPCでサーバの実行環境を再現できるのは本当に便利です。

コンテナ内をGPU対応する

dockerコンテナ内のubuntuでGPUを使うための設定を紹介します。ホスト側はGPUの設定ができてる前提で書きます。

まず、NVIDIA Container Toolkitをインストールします。

これはLinuxのDistributionごとにURLが変わるので、まずコンテナ内のLinuxのDistributionを見なければいけないです。

# cat /etc/os-release

とすれば、OS情報が見れます。この中のIDとVERSION_IDをくっつけたものがDistribution名になります。

bashであれば、下記のコマンドで distributionという変数にDistribution名を格納できます。

# distribution=$(. /etc/os-release;echo $ID$VERSION_ID)

そしてこの distribution変数を使って、curlでapt用のパッケージリストをとってきます。

# curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
# curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list

root権限があれば、下記のコマンドでapt installできます。(なければsudoで)

# apt update && sudo apt install -y nvidia-container-toolkit
# systemctl restart docker

これで、dockerコンテナ起動時に、–gpusオプションをつければ、コンテナ内でGPUが利用できるようになっています。コンテナ内で nvidia-smiコマンドを実行して、GPU情報ができくれば大丈夫です。

docker-composeでGPUコンテナを起動

docker-composeでコンテナを作成する場合は、–gpusオプションと同等のオプションが無いため、もう少しやることがあります。

まず、nvidia-container-runtimeをインストールします。

# apt install nvidia-container-runtime

設定ファイルを /etc/docker/daemon.jsonに作成します。

{
  "runtimes": {
    "nvidia": {
      "path": "nvidia-container-runtime",
      "runtimeArgs": []
    }
  }
}

これで、docker-composeの設定ファイルで runtime: nvidia ができるようになります。

docker-compose.ymlの例を記載します。

version: '2.3'
services:
  [service_name]:
    image: [image_name]:[tag_name]
    container_name: [container_name]
    runtime: nvidia
    environment:
      - NVIDIA_VISIBLE_DEVICES=all
    tty: true
    ports:
      - 80:80
      - 443:443
    volumes:
    - /var/host:/var/docker

これで、docker-composeでコンテナを立ち上げて、コンテナ内で nvidia-smiを実行し、GPU情報が見れればコンテナ内でもGPUが使えます。

M1のMac対応

MacのCPUが進化して、InterのCPUからApple SiliconのCPUに変わりました。これに伴い、docker環境の構築も少し変わります。

まず、Apple SiliconのCPUになったことにより、これまで Intel CPU用のみに開発されたアプリやライブラリを利用するためには、Rosetta 2というソフトウェアをインストールする必要があります。このRosetta 2はバックグランドで動作し、Intel CPU用のバイナリをApple Silicon用に変換してくれます。

これがあれば、Intel CPUでしか動かないアプリも、M1のMacで利用できるようになります。インストールは最初に必要になった時に、ダイアログがでてきてインストールできます。ですが、Rosetta 2を入れていても動かないアプリもあります。

前置きは長くなりましたが、docker環境の構築もM1を利用する場合は、今までと少し変わる部分があります。docker自体はM1対応のものがありますので、それをインストールすれば良いのですが、コンテナやその中でビルドするときに少し気を使います。

まず、コンテナですが、Intel CPU用に作った既存のコンテナイメージですが、一応動きます。ですが、高度な演算などをするととても遅いです。Apple Siliconでかなり速くなるはずですが、旧Intel PCよりも遅くなります。変換のオーバーヘッドですかね。

ですので、高度な演算処理をする場合は、イメージを Apple Silion用のイメージから作り直した方が良いと思います。ubuntu等は、Intel用のイメージだけでなく、M1用(ARM用)の”aarch64″のイメージもあります。ここからイメージを作り直せば、M1の性能を引き出せる素晴らしいコンテナが出来上がります。

また、コンテナ内でビルドなどをする場合に、デフォルトだとIntel CPU用にコンパイルしてしまうものもあります。そんな場合は、rustcというソフトウェアをインストールし、configure時に –build=armを指定すれば大丈夫です。

# apt-get install rustc
# ./configure --build=arm

これで、機械学習のような行列演算がたくさんのソフトもM1のdockerコンテナで快適に動作できます。