yikegaya’s blog

仕事関連(Webエンジニア)と資産運用について書いてます

「ゼロから作るDeep Learning」を読み返した

一度ざっと読んだものの理解できず本棚に放り込んだままになってたオライリーの「ゼロから作るDeep Learning」を読み返した。

文系卒で微分や行列計算などは習っていないけどその知識は必要そうなので高校生向けの本をざっと読んでおいた。

数学知識は付け焼き刃なんでまだ理解できてないところも正直あるんだけど、勉強がてら全体の流れをざっくり整理して書いてみる。

内容

なるべくライブラリを使わずディープラーニングで画像分類器を作るという内容。

同じく機械学習の本でこの間読んだ「自然言語処理アプリケーション開発入門」だとKerasというGoogleが作ったTensorflowという機械学習のラッパーライブラリを使ってディープラーニングを実装していたので実践的ではあったもののブラックボックスだった部分は多かった。この本はその部分の理解を助けてくれるような内容。

画像の扱いについて

ディープラーニングの実行には入力データを数字の配列(多次元配列)として渡さないといけない。

自然言語処理の場合は単語を辞書に登録したり出現頻度調べたりベクトル化のために手間がかかったけど今回扱う画像データの場合はそもそも縦、横、チャンネルの多次元配列の構造になっているのでその点はあまり気にする必要がない。

パーセプトロン

まず基本となるのがパーセプトロンという入力値に重みをかけて0か1の数字を出力するアルゴリズム

この本ではパーセプトロンを使ってAND、OR、XORなど2進数計算からなる論理回路の実装が説明される。

パーセプトロンでのAND回路実装例

def AND(x1, x2):
  w1, w2, theta = 0.5, 0.5, 0.7
  tmp = x2*w1 + x2*w2
  if tmp <= theta:
    return 0
  elif tmp > theta:
    return 1

これにバイアスを足して重みを変えるとORやNAND回路も作れる。

XOR(入力が[0, 0]か[1, 1]の時に0を出力してそれ以外は1を出力する回路)の場合はANDのように単純に実装はできないけど、パーセプトロンを多層にすることで実装ができる。

def XOR(x1, x2):
  s1 = NAND(x1, x2)
  s2 = OR(x1, x2)
  y = AND(x1, x2)
  return y

このように層を複数重ねたパーセプトロンを多層パーセプトロンという。多層パーセプトロンを使うと複雑な問題も解決でき理論上コンピュータそのものも作ることができる。

ニューラルネットワーク

パーセプトロンは複雑な問題も解決できるが、重みを人の手で設定しなければならない。ニューラルネットワークではこの重みを設定する作業をデータから自動で学習させることができる。(パーセプトロンニューラルネットワークの定義は記事や本によってまちまちな気もする)

活性化関数

活性化関数とは「閾値を境として出力が切り替わる関数」のことで「パーセプトロンの場合はステップ関数を実行している」と説明できる。

ステップ関数

ステップ関数の単純な実装

def step_function(x):
  if x > 0:
    return 1
  else:
    return 0

シグモイド関数

ニューラルネットワークではこの活性化関数にステップ関数以外の関数を使う。パーセプトロンニューラルネットワークの違いはこの活性化関数のみ。ニューラルネットワークでよく使われる活性化関数の一つにシグモイド関数がある。

numpy(np)を使った実装例

def sigmoid(x):
  return 1 / (1 + np.exp(-x))

exp関数は例えばexp(1)とすると2.71828183、exp(2)とすると7.3890561を返す。

ネイピア数と指数関数 │ Python 数値計算入門

ステップ関数とシグモイド関数の入力値をx軸、出力値をy軸としてグラフで描画するとステップ関数は0を境に急速に出力が変わるのに対してシグモイド関数は滑らかな曲線を描く。

また、ステップ関数は0か1を出力するのに対してシグモイド関数は「0.731...」、「0.881...」などの実数を返す。

ReLU関数

シグモイド関数ニューラルネットワークの歴史で古くから使われてきたが、最近ではReLU関数が使われることが多い。 ReLU関数は入力値が0以上ならそれをそのまま出力し0以下なら0を出力する関数

def relu(x):
  return np.maximum(0, x)

シグモイド関数を使ったニューラルネットワークの実装例

# indentity_functionは仮置き
def indentity_function(x):
  return x

def sigmoid(x):
  return 1 / (1 + np.exp(-x))

def init_network:
  network = {}
  network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
  network['b1'] = np.array([[0.1, 0.2, 0.3])
  network['W2'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
  network['b2'] = np.array([0.1, 0.2])
  network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
  network['W3'] = np.array([0.1, 0.2])
  
  return network

def foword(network, x):
  W1, W2, W3 = network['W1'], network['W2'], network['W3']
  b1, b2, b3 = network['b1'], network['b2'], network['b3']

  a1 = np.dot(x, W1) + b1
  z1 = sigmoid(a1)
  a2 = np.dot(z1, W2) + b2
  z2= sigmoid(a2)
  a3 = np.dot(z2, W3) + b3
  y = identity_function(a3)

  return y

network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
print(y)

init_networkで重みを初期化。その後「np.dot」でnumpyを使って行列計算を実行し入力値に対して重みとバイアスをかけていく。 その結果を活性化関数(この場合はシグモイド関数)に与えて重みを調整する。

それを繰り返して最適な重みを出力する。

indentity_functionは出力層の活性化関数でこの例では入力をそのまま出力するのみ。出力層の活性化関数については後述。

出力層の設計

入力値に重みとバイアスをかけて結果を出力し、その結果を活性化関数が受け取って重みを何層かに渡って調整する。その後、結果をシステムの目的に応じて選択された関数に与えて出力する必要がある。

上記のindentity_functionのような入力をそのまま返す関数は恒等関数と呼ばれる

ニューラルネットワークはも回帰問題にも分類問題にも使うことができ、回帰問題の場合は恒等関数。分類問題の場合はソフトマックス関数が使われる。

回帰問題とは連続値などの値の予測のことで売り上げ、株価予測のシステムで採用されたりする。

今回の画像識別は分類問題になるのでソフトマックス関数を実装する。

ソフトマックス関数

def softmax(a):
  exp_a = np.exp(a)
  sum_exp_a = np.sum(exp_a)
  y = exp_a / sum_exp_a
  
  return y

softmax関数の使用例

>>> a = np.array([0.3, 2.9, 4.0])
>>> y = softmax(a)
>>> print(y)
[0.01821127, 0.24519181, 0.73659691]
>> np.sum(y)
1.0

このようにソフトマックス関数は0から1の間の実数値を出力する。このおかげでソフトマックス関数の出力値は「確率」として解釈できる。

この例では3番目のクラスである確率が最も高く約73%なので3番目のクラスに分類される。など

ニューラルネットワークの学習

上記の処理で重みを調整することができるが、その重みが正しいか判定するために損失関数を使う。

ニューラルネットワークの学習では、ある「ひとつの指標」を手がかりにパラメータを探索する。ニューラルネットワークの学習で用いられる指標は「損失関数」と呼ばれる。

この損失関数は、任意の関数を用いることができるが、一般には二乗和誤差や交差エントロピー誤差などが用いられる。どっちも正解データとニューネットワークの出力結果の誤差を判定できる関数

機械学習の問題は、「訓練データに対する損失関数を求め、その値をできるだけ小さくするようなパラメータを探し出す」ということ。

なぜ認識精度ではなく損失関数を指標にするのかというと、パラメータの微分がほとんどの場所で0になってしまい、パラメータの更新ができなくなってしまうから。

重みのパラメータを少しだけ変化させたときに正解にどれだけ近づいているのかを判定したいので、ある瞬間の変化の量を表す数値微分を使いたいが、そのためには損失関数を指標にする必要がある。

誤差逆伝播

重みパラメータに対する勾配を数値微分ではなく「誤差逆伝播法」という手法で求めると計算の効率が良くなる。で、この誤差逆伝播法について1章分割かれてるけど正直わからん。多分微分についてちゃんと理解してリベンジしないと無理そうなので誤差逆伝播法の詳細については略。

CNN

画像認識や音声認識など至る所で使われる畳み込みニューラルネットワーク(CNN)というニューラルネットワークのモデルがある。 これまで見てきたニューラルネットワークと同様でレゴブロックのようにレイヤを組み合わせて作ることができるが、新たに「Convolutionレイヤ(畳み込み層)」と「Poolingレイヤ(プーリング層)」が登場する。

これまで見てきたニューラルネットワークでは全結合層(Afflineレイヤー)を用いていた。全結合層はデータの形状が無視されてしまう問題がある。入力データが画像の場合は縦、横、チャンネル方向の3次元の形状だが、全結合層に入力するときには3次元のデータを平らな1次元にする必要がある。

空間的に近いピクセルは似たような値であったり、RGBの各チャンネル間にはそれぞれ密接な関わりがあったり、距離の離れたピクセル同士はあまり関わりがなかったりなど3次元の形状には汲み取るべき本質的なパターンがあるが、全結合層ではそれが無視されてしまう。

一方、畳み込み層(Convolutionレイヤ)は形状を維持することができる。

その後のプーリング層は縦横方向の空間を小さくする。識別に不必要な特徴量をそぎ落として識別に必要な特徴量を抽出する。

その他

GPUとかディープラーニングの歴史、応用事例の話題もあったけどこの記事書いたのはディープラーニングを使って画像分類器を実装する方法を整理したかったからなんでその辺りは略

終わりに

活性化関数や損失関数についてはこういったユースケースではこの関数を使うとうまい結果が得られるよ。。という知識は得られたけどどうしてその関数だとうまくいくのか理解がぼんやりしている気はする。数式とコードが詳しく記載されてるんだけどちょくちょく読めない数学記号とかがあって辛い。この辺り理解するにはやっぱり数学ちゃんとやった後にまた読み返さないとダメそう。

数字ベクトルを入力⇨重みとベクトルをかける⇨活性化関数で出力を調整する⇨損失関数で重みを調整して最適な重みを導く。。という仕組みだったり精度を高めるための手法にどんなものがあるのかざっくり知れたんでとりあえずいいや

「自然言語処理アプリケーション開発入門」を読んだ

普通のバックエンドエンジニアでも機械学習に関わる機会は割とありそうだしちょっと調べとくか。。

と思って機械学習周りの本いろいろ読んでたんだけど、今の自分の前提知識だと「自然言語処理アプリケーション開発入門」がちょうどいい感じだった。

https://www.amazon.co.jp/dp/B07YP84B1R?btkr=1

読んだ段階での自分のスキル

  • プログラム実装は毎日やってるが実際の仕事での機械学習自然言語処理を使った機能実装は未経験
  • 文系卒ということもあり機械学習分野で使われる微分、確率統計、線形代数などの数学知識が薄弱。一応独学で大学数学の入門書をざっと読んだ程度

どんな人向きか

これより先に読んだ「ゼロから作るdeep-learning」だとライブラリを使わずにディープラーニングを実装する。っていう本でちゃんと理論を理解するには良書なんだけどそれに比べて「自然言語処理アプリケーション開発入門」は大分実践的な印象。理論よりも手を動かして作るのが好きなエンジニアにはウケのいい内容だと思う。

数式を使ったアルゴリズムの説明は出てこず、sklearnやkerasといった機械学習ライブラリを使ってとにかくプログラムを作っていく。なので機械学習の詳細な実装を知りたければ他の本を読まないといけないけど、その代わり精度を上げるための手法だったり具体的なデータの集め方など実際の現場で使えそうな内容が多かった。

内容

最初から最後まで基本的には自然言語処理の手法を使って対話エージェントを作る内容。最初は理解しやすい手法から始まって少しずつ精度を上げる手法が紹介され最終的にニューラルネットワークを使った手法にして。。といった具合に徐々に難易度を上げていくような進め方をする。

以下復習がてら本書での対話エージェントの作りをざっくり書いてみる。

対話エージェントの仕組み

質問をクラスに分類する

まず、似たような質問を「クラス」としてまとめる。例えば

自然言語処理をしたいんだけど、どうすればいい?」や「日本語の自然言語処理について知りたい」

といった質問は「自然言語処理の学習について聞く質問」として「クラス0」とする

「好きなプログラミング言語は?」や「どの言語を使うのがいいかな?」

は「おすすめのプログラミング言語を聞く質問」として「クラス1」とする

で、それぞれのクラスに対する回答を用意する。例えばクラス1の「好きなプログラミング言語は?」に対しては「Pythonがおすすめです」など。

「ただこの文章入力に対してはこの回答をします」といったルールを愚直に登録するやり方には無理があるので入力される文の傾向をシステムに学習させて「どのような質問文がきたらどの回答文を返せばよいか」のパターンを取得していく。

つまり「文を入力すると、その文が属するクラスを予測し、クラスIDを出力するシステム」を作れば対話エージェントが成立する。

以下文が所属するクラスを予測する具体的な手法

まず入力された文書を「ある決まった個数の数字のまとまり」に変換する必要がある。「ある決まった個数の数字のまとまり」を以下「ベクトル」と表記する。

ベクトルの作り方

文章をわかちがきして単語に分ける→Bag of Wordsという手法で単語の出現回数をカウントする

Bag of Wordsはまず単語を辞書に登録して登録された単語ごとの出現数をカウントする特徴抽出の手法

辞書の例

単語 番号
0
あなた 1
ラーメン 2
好き 3
こと 4
5
6
7
8
です 9

ベクトルへの変換例

# 私 / は / 私 / の / こと / が / 好き/ な / あなた / が / 好き / です
[2, 1, 0, 2, 1, 2, 1, 1, 1, 1]

# 私はラーメンが好きです
[1, 0, 1, 1, 0, 1, 1, 0, 0, 1]

この本ではわかちがきの実装にはmecabという形態素解析のライブラリを使った実装が記載されていて、BoWはスクラッチでの実装とscikit-learn を使った実装が記載されている。

SVMを使った識別器

次に上記で得られたベクトルを使って識別器を作る。特徴ベクトルを入力し、クラスIDを出力することを「識別」と呼びそれを行うオブジェクトや手法を「識別器」という。

まずこの本ではSVMsupport vector machine)という手法で識別器の実装を進めていくけどSVMについての詳細説明は省略されていてscikit-learn(ライブラリ)を使ったSVMの使い方が説明されている。

まあ、とにかくベクトル作ってscikit-learnのプログラムに与えると文章に対する機械学習でのクラスIDの予測ができる。

チューニング

TF-IDF

ここまでではBoWで特徴ベクトルを計算していたけど、BoWの代わりにTF-IDFを使うことで精度を向上させることができる。 TF-IDFは「重要な単語の寄与を大きく、重要でない単語の寄与を小さくする」ことでBoWよりも文の特徴をよく表現できる。

例えば「私はラーメンもお好み焼きも好きです」、「私はラーメンが好きだ」、「私はサッカーも野球も好きだよ」という文章があったとしてこの文章を特徴づけているのは「ラーメン」「お好み焼き」「サッカー」「野球」といった「私の嗜好」を表す単語だが、BoWでは「私」や「好き」といった他の文章との比較において大して意味のない単語と同等に扱われてしまう。その問題をTF-IDFでは解決できる。

チューニングその他

文章の表記揺れや文字コードのズレを前処理で吸収する方法だったり、わかちがき部分のその他の手法だったり、SVM以外の識別器(決定木アンサンブル、k近傍方)の紹介だったりと分類の精度を高める方法が紹介されるけどそれ全て書くのはきついので略。

ニューラルネットワーク

内容の後半辺りからニューラルネットワークの説明とそれを使った識別器の実装についての説明に入る。

ニューラルネットワーク」っていうのは世間で「AI」、「人工知能」という言葉を聞いた時によくイメージされる脳細胞を模して設計された計算モデルのこと。その内容を数学的なモデルに落とし込んだものを「パーセプトロン」という。

n個の入力とそれぞれに対応した重み、固定値の入力バイアスからなる。

こんなやつ(wikipedia)↓

ja.wikipedia.org

1つのパーセプトロンは数値を1つ出力するので複数並べると全体として1つのベクトルを出力する。このまとまりを「レイヤー」という。

このレイヤーを増やして高い精度を出せるようにした手法が「ディープラーニング」と呼ばれる。

で、このニューラルネットワークを識別器として使うことができる

追記

本書ではニューラルネットワークパーセプトロンについて上記のように書いてあったけど、他の本や記事ではノードの活性化関数にステップ関数を使ったのがパーセプトロンで、シグモイド関数のような滑らかな活性化関数を使ったのがニューラルネットワークと呼ばれてたりする

ニューラルネットワークを使った識別器

単純パーセプトロンを使ったクラス分類

以下単純パーセプトロンを使って2クラスに分類する具体的なイメージ(自分なりの解釈でベクトルや重みの値はサンプルコードから転用した適当な値)

まずTF-IDFを使って入力文書をベクトル化して入力し、その入力に対して重みとバイアスをかけてを実行すると0から1の間の数字が出力される。例えば「好きなプログラミング言語は?」、「自然言語処理をしたいんだけど、どうすればいい?」みたいな質問文を用意してベクトル化して[0.2, 0.3, -0.1]、[-0.2, -0.1, 0.9]という結果が出たとする。これをパーセプトロンの入力値とする。

で、重みを[-1.64, -0.98, 1.31]、バイアスを「-0.0.5」だとすると入力ベクトルに対して 0.2 * -1.64、 0.3 * -0.98‥‥といった風にそれぞれの値に重みをかけた後にバイアスの-0.05を足した値が出力される。

で、その出力が例えば「0.3093841557917043」と「0.8256347143825868」だとする。そして数字が0に近ければクラス0、数字が1に近ければクラス1といった風にクラス分けして識別器として使うことができる。

層を深くする場合、他クラス分類の場合

上記の例だと2クラスのどちらかにしか分類できないので実用できない。他クラスの場合は出力値をクラスIDの数だけ出力して最も1に近いクラスに分類する方法が紹介されている。例えば出力値が[0.33898331, 0.65419141, 0.498738291]だとすると2つめが最も1に近いので2つ目のクラスに分類する。

また、ニューラルネットワークの層を深くして流行りのディープラーニング化すると精度の高いシステムができるけどただ層を深くするだけではダメで実現のためには様々な問題を乗り越えなければいけないんだけど、この記事は全体の流れをざっくり整理したい。。と思って書いたんでその辺りの地道なチューニングの話は略。

Nuxt.jsとPython FastAPIで作ったブログサービスを無料デプロイした

NuxtとPython FastAPI、Postgresqlでブログサービスをサクッと作ってVercelとHerokuで無料デプロイしてみた

作ったもの

blog-app-frontend-red.vercel.app

ソースコード

github.com

github.com

作った動機

ブログサービスは昔Railsで作ったことあるんだけど、VueかReact使ってSPAでまた作りたいなーとぼんやり思ってたので。 あとPython FastAPIが最近評判良さそうで使ってみたかった。

2021/8/29現在実装した機能

  • ユーザ登録、ログイン
  • 記事投稿
  • 記事の詳細確認

まあ、とりあえず基本機能ができた程度の進捗。

デプロイ手順

FastAPIのherokuへのデプロイ

FastAPI用のDockerイメージを開発環境用に使ってたんでそのイメージを使い回してHerokuにデプロイしたいんだけど、ただ開発環境では開発用のシェルで実行していたんで本番環境ではそこを外したイメージを用意した。

FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7

RUN /usr/local/bin/python -m pip install --upgrade pip

COPY ./app /app
RUN pip install -r requirements.txt

↓本番用のDockerではこれを外す
CMD /start-reload.sh

heroku.yml

build:
  docker:
    web: Dockerfile.production

heroku.ymlを用意したらFastAPIのプロジェクトフォルダで以下のコマンドを実行するとデプロイできる

# herokuにログイン
heroku login
# アプリケーション作成
heroku create
# Docekrコンテナでデプロイするよう設定
heroku stack:set container
# デプロイ実行
git push heroku main

また、Postgreのデータベースを作成してテーブルを作成する(テーブル作成用のプログラムは現状作ってないので手動のSQLで作っておく)

## 無料のデータベース作成
heroku addons:create heroku-postgresql:hobby-dev
# herokuのコマンドプロンプトを立ち上げてそこからSQL実行
heroku pg:psql

Herokuの注意点

  • Heroku上でアプリケーションのポートを保持するPORTという環境変数がデフォルトで作られているのでプログラム上それを使用するようにしておく(自分で80とか3000とかポート指定できないっぽい)
  • PostgreのURLが設定されているDATABASE_URLという環境変数も作られているのでPostgreへの接続にはその変数を使用するようにしておく

NuxtのVercelへのデプロイ

VercelでのデプロイはNuxtの場合簡単。

作業としてやることは

  1. vercel.jsonを用意してNuxtのルートディレクトリに置いておく
  2. GithubにpushしてVercelの管理画面上でレポジトリ選択してデプロイ実行
  3. 環境変数でバックエンドのURLを指定しているのでVercelの管理画面上からPython FastAPIをデプロイしたherokuのURLを設定する
  4. デプロイ後、VercelのURLが発行されるのでFastAPIのCORSでそのURLを許可する

vercel.jsonはこんな感じ

{
  "builds": [
    {
      "src": "nuxt.config.js",
      "use": "@nuxtjs/vercel-builder",
      "config": {}
    }
  ]
}

Vercelの注意点

ドメインが複数発行されるようなので全てCORSに追加しといた方がいいかも。1つしか使わないなら1つだけ指定でもいいけど

今後やりたいこと

  • ソーシャルログイン
  • 記事のタグ付け
  • ユーザのフォロー機能
  • マークダウン対応
  • ユーザ登録はセッション周りはちゃんと作ってないので改善する?
  • 記事へのタグ付け
  • 画像や動画の埋め込み対応 。。。

などなどやろうと思えばいくらでもあるけどまあ、基本機能の実装とデプロイまでやったら割と気が済んでしまったな。。NuxtとFastAPIでちょっと遊びたかっただけだし

また気が向いた時に作るか

久しぶりにrails newしたので設定作業メモ

最近仕事でrails newを実行するとこからAPIサーバ2つ作った。その際の設定作業メモ

使った技術やバージョンざっくり

key val
Dockerのbaseイメージ ruby:3.0.2-alpine3.14
Railsのバージョン 6.1.4
DB 片方がPostgresql、もう一方がMySQL

注意点

  • rails newする時にオプションをつけないと初期設定が面倒
  • rubyのイメージがalpineの場合、postgre、mysqlへの接続用ライブラリをdockerイメージ内で追加する必要あり
  • 別のdockerコンテナで立ち上がってるサービスにアクセスするにはconfigでdockerのDNSからのアクセスを許可する必要あり

rails newする時にオプションをつけないと初期設定が面倒

デフォルトだと要件に合わないのでオプションをつけてrails newすると初期設定が大分楽になる

  • デフォルトだとDBがSQLite→今回はPostgreSQLMySQLを使いたい
  • デフォルトだとテストフレームワークがminitest→rspecを使いたい
  • デフォルトだとerbやアセットパイプラインなどフロントフロントエンド開発に必要なライブラリもインストールされる→今回APIサーバが欲しいだけなのでいれたくない

実行したコマンド

rails new samle-api-app --api -d postgresql --skip-test

-dオプションでDB接続に必要なライブラリの追加、--skip-testでminitestのインストールスキップ、apiオプションでフロントエンドライブラリのインストールをスキップしてくれる。

rubyのイメージがalpineの場合、postgre、mysqlへの接続用ライブラリをdockerイメージ内で追加する必要あり

Dockerfileに以下追加が必要。alpineの場合これがないとDBに接続できない

# PostgreSQLの場合
RUN apk update \
    && apk add \
    postgresql-client \
    postgresql \
    postgresql-dev

# MySQLの場合
RUN apk update \
    && apk add \
    mysql-client \
    mysql-dev

別のdockerコンテナで立ち上がってるサービスにアクセスするにはconfigでdockerのDNSからのアクセスを許可する必要あり

別のDockerコンテナにhost.docker.internalというDocker内のDNSで接続したかったんだけどやってみると403エラーになった。 調べたところRails6からconfigに設定追加しないと接続できないらしい。

config/environments/development.rbに以下追記

config.hosts << "host.docker.internal"

これでこんな感じでHTTPリクエスト送ったりできる

Faraday.get("http://host.docker.internal:3000/api/v1/sample")

参考

qiita.com

「CPUの創りかた」を読んだので軽く感想

積読になっていた「CPUの創りかた」を読んだので軽く感想書いてみる

book.mynavi.jp

どんな本か

  • LEDを光らせることができる4bit CPUを作成する手順を書いた本。C言語のプログラムを実行できます。。みたいなところまではやらない。
  • 入門本なので読みやすい。CPUの仕組みっていうテーマは難しめだけど、表紙や挿絵が萌え絵だったり文体が緩かったりで緊張感なく読める
  • 配線作業など電子工作の「実際の製作方法」は書いていない。実際に作らずに読んだだけだけどそれでも満足感はあった。
  • 書かれた年は2003年らしく割と古いけどあんまり気にならない
  • クロックとかプログラムカウンタとか命令デコーダとかレジスタとかなんとなく存在は知ってたけどよくわかってなかった仕組みが物理レベルでどう動いているかわかる
  • CPUが直接実行できる機械語論理回路で表現する方法がわかる

感想

CPU設計の仕事とかしたことないし、CPUに近いシステムプログラミング、組み込みプログラミングの分野でもないんだけどブラックボックスだった部分の知識を学ぶのは自信につながってくる気がする。

あと実益よりも単純に好奇心満たせて面白い。っていうのがあるんで読む理由はそれで十分かな。。

機械語をCPUがどう扱ってるのかはぼんやりわかったけど、C言語みたいな高級言語機械語にする部分とかも気になってきたな。。それはまた別の話でコンパイラの本とか読まないとわからなそう。

ECS環境で504Gatewayエラーに軽くハマった

ECS環境構築用のterraformの設定値を変数に切り出す作業をしてたんだけど、504Gatewayエラーが出るようになって軽くハマったのでメモ。

調べたところこの記事の内容とほぼ同じ事象だったらしくVPCのCIDRブロックを変数に切り出した際にセキュリティグループの更新を忘れていたらしい。。

yoshinorin.net

気づけばなんてことない内容なんだけど、ECSのイベントやCloudwatchにそれらしいエラーが何も出ないので気づきにくいな。。504だけしか情報がないと対応辛い。

resource "aws_security_group_rule" "hoge-ecs" {
  security_group_id = aws_security_group.hoge-ecs.id

  type = "ingress"
  from_port = 80
  to_port   = 80
  protocol  = "tcp"

  # ここを変数に書き換えるのを忘れてた。。
  cidr_blocks = [var.vpc_cidr_block]
}

Rails+ECS環境でのassets:precompileのタイミング

ECS環境でRailsを動かす時にassets:precompileを実行するタイミングとして、最初とりあえずコンテナ起動時に実行してたのを、Dockerイメージをbuildする時に実行するように変更したんだけど割と面倒だったのでメモ。

configパス以下を読んでいるらしくconfig以下の環境変数がないと落ちる。

例えばこういうのがないと実行できない↓(database.yml)

  username: <%= ENV.fetch("DATABASE_USERNAME") %>
  password: <%= ENV.fetch("DATABASE_PASSWORD") %>

環境変数はS3からpullする構成にしていたので、コンテナ起動時に実行すると環境変数が読み込まれているんだけど、build時には環境変数をファイルから読むことができない。

Dockerfileはgitに入れたいしに機密情報書くのやだなー。。と思ってたけど、assets:precompileってフロントエンドのアセット作ってるだけで、DBの認証情報とか必要ないのでbuild時は適当な文字列入れておけば問題なく動いた。

以下Dockerfileに追記

ENV DATABASE_USERNAME dummy
ENV DATABASE_PASSWORD dummy

VOLUMEの指定方法を間違えてコンパイルしたアセットが消える

assets:precompileの結果をnginxのコンテナと共有するためにDockerfileの中でVOLUME指定してたんだけど、VOLUMEはassets:precompileの後に指定しないとホストの内容が反映されてしまうらしくコンパイル後のassetが残らない。

# OK
RUN bundle exec rake assets:precompile RAILS_ENV=production
VOLUME /app/rails/public

# NG
VOLUME /app/rails/public
RUN bundle exec rake assets:precompile RAILS_ENV=production

これもコンテナ実行時にassets:precompileするとassetが消えずに残るので正常に起動できるが、build時だと消える。

地味に嫌だったなこれ。