日商簿記2級合格。可視化とふりかえり
日商簿記2級に合格した
2019/3/27 全国の合格率が発表されたので情報を修正しました。
内定先の会社から入社までに簿記2級を取得するように言われ、日商簿記2級(第151回)を受験した。第151回は、ひどい回だった。過去問で全くみたことのないような問題(しかも、処理量が多い問題)が設問3で出題され、Twitterのトレンドに簿記2級がでるぐらい騒ぎになった。 下記のように簿記の先生が怒ってる動画がでてるw
東京・横浜の商工会議所のデータでは合格率は13.3%となっているが、全国の合格率は12.7%しかなかった。
3/15に結果がでて、合格したことがわかったときは嬉しかった。
簿記2級の合格率の平均は20%-30%といろいろなWebサイトに書いてあったりするが、
平均が全く役に立たないほど合格率の変動が激しい。
標準偏差を計算したり、合格率の変動を可視化したら、簿記2級の異常さがわかるかと思い、Jupyter Notebookを公開した。
Jupyter Notebookの公開にはGoogle Colabを使用した。
ランタイム -> 全てのセルを実行
で可視化プログラムが動く。
可視化にはplotlyを使用しており、カーソルを表の上におくとその年度の合格人数などの詳細な値が出るようになっている。
実行できない(したくない)人のために、下記に画像を貼っておく。
勉強法
150回の試験も受験していたのだが、その時は過去問と実際に出た問題の難易度が違いすぎて落ちた。過去問を中心に勉強していたのがよくなかった。今回は過去問ではなく、TACの問題集とTACの直前講座の予想問題を解きまくったのがよかったと思う。TACの直前講座は動画で解答解説をしてくれるものだったが、解答解説動画は勉強するのに効率がよくなかったから無視した。ひたすら予想問題を解いて採点し、解答解説書を読み、わからないところは教科書・問題集に戻って理解を深めた。修士論文の発表を終えてから、簿記の勉強を再開したため、簿記の勉強をまともにできたのは2週間しかなかったが、なんとかなった。
本気で簿記を勉強した時間は第150回、第151回対策合わせて150-200時間くらいか。
受験中の戦術
まず、どんな問題が出ているか最初に確認した。
3の内容が苦手な上になんかやばそうな気配がしたので、 4 -> 5 -> 1 -> 2 -> 3の順で解くことにした。4(25分)、5(15分)、1(25分)、2(15分)くらいの時間配分で解いて、3には残りの40分を費やした。ひょっとすると見直しをした方が点数が高かったかもしれないが、試験中に「3を切り捨て他の問題を見直す」という判断はできなかった。
感想
なんとか合格点ピッタリ(70点/100点中)で合格したが、なぜかもやもやする。落ちてたら「ふざけるな、ふざけるな、ばかやろう!なにが資格試験じゃボケー」と叫んでいたところだが、合格したので「ふざけるな、バーロー」ぐらいにしておく。
簿記2級は有名な資格試験だから、いろいろな人が受けているだろう。だから、合格率が低いなら難易度が高いと考えてもおかしくはないと思う。難易度が回によって大きく変わる、運ゲーな資格試験はもう受けたくない。課金しても当たるかどうかわからないなんて、ソシャゲのガチャじゃあるまいし。
駄文失礼しました。
2018の振り返り
不運(ハードラック)と踊(ダンス)っちまった2018
2018年の漢字は「災」だと言うニュースをこの前聞いた.
私にとっても2018年は「災」の多い1年だった...
1月 実家に帰省
諸事情あり,今まで居候していた祖父母の家から実家に引っ越した.
都会の生活に慣れてしまっていたため,田舎での生活は不便で仕方なかった.
家から駅までは30分くらいあるし,大学まで往復5時間かかるのはもう発狂しそうだ
1月 愛犬の病気
去年の1月,愛犬が死ぬかもしれないと言う状況になった.
いつも食欲旺盛な愛犬が,餌をほとんど食べてくれなくてだいぶ焦った.
胆のうに石ができたとのことで,その石を取り除く手術をしなければならないが,その手術は非常に難しいものとなると医者は言っていた.
本犬の努力と点滴のおかげで手術は避けることができたのは幸いであったが,不安はまだなくなったわけではない...
3月 自転車事故
まったく最悪だった.
通学途中で単独で自転車事故をおこしてしまった.幸いだったのは,受け身をとったことで骨折などの重症にならなかったことと,自分以外の誰も怪我をさせなかったことだ.ただ,顔面を地面で擦ってしまい,血だらけになり,顔を何十針も縫うことになり,しばらく体力が戻らなかった...
就活のスケジュールの変更しなければならなくなったのもいたかった. 就活はなんとか第一希望群の1つから内定をいただけたことは幸いであった.が...
11月 簿記
内定をくれた会社は入社の条件として,入社までに簿記2級に合格することを条件としていた.効率よく勉強を進めるために,過去問を中心に勉強したが,穴があった.その穴が試験に出てしまい,大きく失点した.さらに日商簿記は難易度の難易度の変化が激しく,回によって全く難易度が異なる.簡単な会は40%以上が合格し,難しい回は合格率が10%程度になる.私が受けた回は合格率が15%にとどかない,難易度の高い回だった.
やれやれ入社が少なくとも3ヶ月遅れることが確定してしまった.
さて
2018年はついてない年だったが.2019年は良い年になるだろうか
目が点! すっごく小さいDockerイメージ! tweetコマンドをDockerを使って動くようにした
世の中のDockerイメージは大きすぎて、ダウンロードに時間がかかりすぎる!(某CM風)
Dockerイメージを作っていると悩まされるのがDockerイメージ(以下イメージ)のサイズです。イメージサイズが大きいとダウンロードやアップロードに時間がかかり、不便です。
そこで、テザリングでパソコンをインターネットに繋げているときにダウンロードしても、通信量・通信時間が気にならないような大きさのイメージを作りました。
https://hub.docker.com/r/kotaru/tweet/
その大きさはなんと、3MB!
"目が"点ですよ。メガだけに!
はぁぁぁぁい!ずっと言いたくて言えなかったダジャレを言ったところで、このイメージを作った過程を説明します。なにをしたかをとっとと知りたい人はまとめを見ましょう。
tweet command written in Go
Go言語を覚えたてのころに作ったtweetコマンドですが、最近更新しました。Dockerを使って、Go言語の開発環境がなくても簡単に使えるようにしました。
なお、動かすためにはTwitterのAPIキーが必要で、環境変数にセットする必要があります。詳しくは上記のリンクで。
tweetコマンドを作った背景
このコマンドを作った時は、まだ研究室にSlackが導入されていませんでした。サーバで長期実行プログラムが終わったときに、通知するいい方法がなかったので、このコマンドを作りました。
機能はシンプルで、「標準入力から受け取った文字列をそのままツイートする」だけです。案外便利なので今でも普通につぶやく時に自分は使ってます。後輩に変人だと言われてしまったが...w
(注意) 重要な情報はツイートしないようにしてくださいね
Docker multi stage build with scratch
さてさて、本題といきましょうか。
Docker 17.05からmulti stage buildという機能が追加されています。
詳細はリンク先を参照してください。
簡単に説明すると、アプリケーションをビルドするためのコンテナとアプリケーションを実行するコンテナを分離することができるという話です。
分割することで、ビルド時のみに必要なファイルをわざわざ消すシェルスクリプトを書かなくてすみ、綺麗で保守性の高いDockerfileをかけるようになります。
下記は私の作ったtweetコマンドをDocker上で動かすためのDockerfileです。
FROM golang:1.11.2-alpine3.8 as builder RUN apk --update --no-cache add git ca-certificates WORKDIR /go/src/github.com/kotaru23/tweet COPY . . RUN go get . RUN CGO_ENABLED=0 GOOS=linux go build -o tweet FROM scratch COPY --from=builder /go/src/github.com/kotaru23/tweet/tweet /go/bin/ COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt CMD ["/go/bin/tweet"]
注目するべきは、3点あります。
- FROMが2回書かれている。そのうち1つには "as builder"と書かれている
- COPYの後に"--from=builder"と書かれている
- FROM scratch
説明
FROMが2回書かれていますが、これは決して間違いではありません。
1つ目のFROMから次のFROMまでの間では、tweetコマンドをソースコードからコンパイルしています。また、この後の工程のために"builder"という名前をコンテナにつけました。
FROM golang:1.11.2-alpine3.8 as builder RUN apk --update --no-cache add git ca-certificates WORKDIR /go/src/github.com/kotaru23/tweet COPY . . RUN go get . RUN CGO_ENABLED=0 GOOS=linux go build -o tweet
2つ目のFROM以降は、アプリケーション実行用のイメージです。
1つ目のイメージを継承しているのではなく、全く別のイメージです。
FROM scratch COPY --from=builder /go/src/github.com/kotaru23/tweet/tweet /go/bin/ COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt CMD ["/go/bin/tweet"]
COPY --from=builder "builderイメージのパス" "コピー先のディレクトリ"
とすることで、先ほど作成したbuilderイメージからファイルを持ってきています!
Go言語で書かれたアプリケーションは、外部ライブラリ(C言語等)を使用していなければ、バイナリ1つをコピー&ペーストすれば実行できます。(バイナリは各OS, CPUのアーキテクチャごとにコンパイルする必要があります)
Go言語のライブラリ、ランタイムなどは全てバイナリの中に含まれているようです。
このtweetコマンドはGo言語で書かれていてバイナリとCA証明書があれば動くようになっているので、先のbuilderコンテナからバイナリとCA証明書を実行用コンテナに持ってきています。
FROM scratch
scratchとは、ほとんど何もないオフィシャルイメージです。/bin/shすらないようです。詳しくは下記リンク
https://hub.docker.com/_/scratch/
このイメージはファイルを他の場所からコピーしてきて、バイナリを実行するぐらいの用途にしか使えないのではないかと思います。JavaなどのVMを使用する言語で書かれたアプリケーションでは使えないかもしれません。しかし、外部依存のない純粋なGo言語で書かれたアプリケーションであれば、バイナリを持ってくるだけで実行できます。今回の例では通信をするためにCA証明書もビルド用のコンテナから持ってきていますが、それだけです。
まとめ
3MBのイメージを作るために必要な3つのこと
- Go言語で書かれたアプリケーション(C言語の依存なし)
- Docker multi stage build
- scratchイメージ
あとがき
あ、"3"って数字使いすぎですかね。コンサルタントの真似ですw
それとBuildKitの話ができなかったのが残念です。
最後に言わせてほしい。
$ echo "純子ちゃん、やーらしか!" | \ docker run --rm -i \ -e TWITTER_CONSUMER_KEY=$TWITTER_CONSUMER_KEY \ -e TWITTER_CONSUMER_SECRET=$TWITTER_CONSUMER_SECRET \ -e TWITTER_ACCESS_TOKEN_KEY=$TWITTER_ACCESS_TOKEN_KEY \ -e TWITTER_ACCESS_TOKEN_SECRET=$TWITTER_ACCESS_TOKEN_SECRET \ -e TWITTER_SCREEN_NAME=$TWITTER_SCREEN_NAME \ kotaru/tweet
近況報告というか簿記に対するぼやき
この文章は頭の中のもやもやをそのままアウトプットしていて理路整然とはしていないかもしれない。あしからず
近況報告というか簿記に対する愚痴
前回の投稿からだいぶ時間がたってしまった。
というのも、ここのところ猛烈に忙しい。
まとめると下のような感じ
- 内定先の企業から簿記の2級を入社前にとるように言われたので、簿記の2級の勉強をして、受験した
- 大規模なデータを扱うためのハイパフォーマンスなプログラムを作成した
- ディープラーニングをするためのモデルを構築している
特にやばいのは1の簿記の勉強だ。 考えるとき以外は持たないシャーペンと消しゴムを握りしめ、いつもの生活でやらないような暗記をし、プログラム書けば良さそうなタスクを、しょぼい電卓で計算しなければならないのは控えめに言っても苦痛でしかない。Jupyter Notebook使いたいw
会計の仕組みを知っておくという点で簿記が重要であるという考えはわかる。ただ、この資格試験おかしくないか? 近年出題範囲の変更(拡大)があって覚えるべきことが増大しているうえに、回によって合格率が大きく異なるようだ。合格率の分散が大きすぎる。 いろいろな立場のひとが受ける試験なので、受験者の質が回によって大きく変わるというのは考えにくく、合格率の変動が難易度の変動とほぼ同期していると考えても問題ないだろう。 難易度を一定に保たないのって、なんでだろう。資格の価値が受験する回によって揺らいでしまうのはおかしくないか?
まあ、そんな簿記必須の企業を就職先として選んだのは自分なので自業自得だし、やるしかないのだが。入社後は良さそう
2, 3に関してもすごく忙しいが、すごく楽しい。簿記の勉強(特に商業簿記)とは異なり論理的な思考が重要で、脳内メモリ消費量が少ない。
ふう。 さらに忙しくなるのはこれからだ。 簿記の勉強をしながら、ビッグデータ処理・機械学習を使う研究をしつつ、修論を書かなければならない。地獄の始まりだ!
余談
日商簿記を開催している日本商工会議所だが、「日商プログラミング検定」を創設するらしい。サンプル問題を見てみてみたが、なんだかなあって感じ。Scratchの操作方法覚えてもなあ... てか、VBAって... そもそもサンプル問題のファイルがENTRYレベルとBASICレベルはPDFなのになぜSTANDARDは画像ファイル(jpeg)なのか。半角英数字と全角英数字がごちゃ混ぜになっているのも、キモチワルイ。生理的に無理
https://www.kentei.ne.jp/pg-effect
駄文失礼しました。
LaTeXのtexファイルをPDFに変換するツールをDockerでつくった
前置き
機械学習のネタを投稿したいが,研究で使っているデータは公開できないし,未発表の研究を投稿するわけにもいかないので,Dockerネタになってしまう.
公開データを使って実験して投稿しようかなあ...
LaTeXの環境構築が辛かった...
本題
研究の中間発表に向けて,レジュメを作り始めたのだが,macOSの環境にTeXの環境を作るのに失敗し,うまくいかなかったのでDockerで作った.
Quick Start
$ docker run --rm \ -v "texファイルがあるディレクトリを絶対パスで記述":/home/geotaru/workdir \ -e target=/home/geotaru/workdir/"マウントしたディレクトリの下にある,コンパイルしたいtexファイルのパス" \ geotaru/latex-compiler
Example
カレントディレクトリにあるresume.texからPDFファイルを生成する
$ docker run --rm \ -v `pwd`:/home/geotaru/workdir \ -e target=/home/geotaru/workdir/resume.tex \ geotaru/latex-compiler
Dockerfile
Dockerfileは下記の通り.
他の人のレポジトリを参考にDockerfileを書き直し,コマンドラインツールを作った.参考にしたURL・レポジトリは最下部に書きました.
alpine Linuxを使って軽量化したが色々追加したせいか,非圧縮で1GBを超えてしまった.
FROM alpine:3.8 MAINTAINER geotaru <geotaru.dev@gmail.com> ENV PATH /usr/local/texlive/2018/bin/x86_64-linuxmusl:$PATH ENV USER geotaru ENV HOME /home/${USER} RUN apk --no-cache --update add bash ENV SHELL /bin/bash RUN adduser -S ${USER} WORKDIR /root RUN apk --no-cache --update add wget perl xz tar fontconfig-dev && \ wget http://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz && \ mkdir install-tl-unx && \ tar xf install-tl-unx.tar.gz --strip-components 1 -C install-tl-unx COPY texlive.profile /root/install-tl-unx/ RUN /root/install-tl-unx/install-tl \ --repository=http://mirror.ctan.org/systems/texlive/tlnet/ \ --profile=/root/install-tl-unx/texlive.profile && \ tlmgr install \ collection-basic collection-latex \ collection-latexrecommended collection-latexextra \ collection-fontsrecommended collection-langjapanese latexmk && \ ( tlmgr install xetex || exit 0 ) && \ rm -rf /root/install-tl-unx && \ apk --no-cache del wget xz tar fontconfig-dev && \ rm -rf /root/install-tl-unx COPY ./compile.sh /opt/compile RUN chmod 777 /opt/compile USER ${USER} WORKDIR ${HOME}/workdir ENV target ${HOME}/target.tex CMD /opt/compile ${target}
compile.shの中身
#!/bin/bash platex $1 dvipdfmx $(echo $1 | sed "s/.tex$/.dvi/g")
上記のDockerfileの最新版は下記URLにおきました. git cloneしてお好きなように改変して自分の端末でbuildするなり,フォークしていじくりまわすといいと思います.ご自由にどうぞ
感想
自分のMacの環境がごちゃごちゃになってしまい,環境構築に失敗してtexファイルをpdfにうまく変換できなかったが,Dockerを使うことで解決した.Dockerはイイゾー!
参考
滅びの呪文を唱えてみた
滅びの呪文唱えてみた
Linuxを破壊するコマンドで、"sudo rm -rf --no-preserve-root /"というものがある。やったことなかったので、家のどうでもいいLinuxでこのコマンドをたたいてみた。雰囲気を出すために、わざわざホスト名を変えたりユーザを作ってやったw
バルス!!
「天空の城ラピュタ」にでてくる呪文をエイリアスとして登録し、コマンドを叩いた。
[Pazu@laputa ~]$ alias バルス='sudo rm -rf --no-preserve-root /' [Pazu@laputa ~]$ バルス あなたはシステム管理者から通常の講習を受けたはずです。 これは通常、以下の3点に要約されます: #1) 他人のプライバシーを尊重すること。 #2) タイプする前に考えること。 #3) 大いなる力には大いなる責任が伴うこと。 [sudo] Pazu のパスワード:
「大いなる力には大いなる責任が伴うこと」
sudoコマンドを初めて叩く時に出るメッセージだが、なんかかっこいいw
大量のエラー
なにやら色々でてきたw sudoでも消去できないのかな?
rm: `/sys/kernel/debug/tracing/events/libata/ata_eh_link_autopsy_qc/enable' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/libata/enable' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/libata/filter' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/drm/drm_vblank_event/format' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/drm/drm_vblank_event/filter' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/drm/drm_vblank_event/id' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/drm/drm_vblank_event/enable' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/drm/drm_vblank_event_queued/format' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/drm/drm_vblank_event_queued/filter' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/drm/drm_vblank_event_queued/id' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/drm/drm_vblank_event_queued/enable' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/drm/drm_vblank_event_delivered/format' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/drm/drm_vblank_event_delivered/filter' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/drm/drm_vblank_event_delivered/id' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/drm/drm_vblank_event_delivered/enable' を削除できません: 許可されていない操作です 中略 rm: `/sys/kernel/debug/tracing/events/filelock/break_lease_noblock/format' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/filelock/break_lease_noblock/filter' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/filelock/break_lease_noblock/id' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/filelock/break_lease_noblock/enable' を削除できません: 許可されていない操作です rm: `/sys/kernel/debug/tracing/events/filelock/break_lease_block/format' を削除できません: 許可され�
ホームディレクトリ消失
bash 「あっはっはっは、どこへ行こうというのかね!?」
cdコマンドがまだ生きているってことかな。
[Muska@laputa ~]$ cd -bash: cd: /home/Muska: No such file or directory
コマンド消失
コマンドが消去されたようだ。
[Muska@laputa ~]$ ls -bash: ls: command not found [Muska@laputa ~]$ whoami -bash: whoami:command not found
感想
「大いなる力」を感じることができた!
GPUサーバを立てた
研究室にDeep Learning用のGPUサーバを立てた
先週半ばにパーツが届き、先週金曜日に後輩の助けを借りつつGPUサーバを組み立てた。今まで、パソコンを組み立てたことがなく、GPUをSLI構成なんて見たことがなかったため、すごく新鮮でいい経験になった。配線に苦戦したので、パーツの選びかたが大事だなと思った。
OSはUbuntuを採用した。その理由は数あるLinuxのディストリビューションの中で、NVIDIAのドライバーをインストールするのがもっとも簡単だと思ったからだ。
以前、NVIDIA Dockerを使ってCUDAドライバをなんとかする方法を投稿したが、その手法は今も使っている。今回のサーバは透過プロキシを利用したため、Docker Hubは必ずしも必要ない。やっぱり必要だった...
ただ、なぜかCNNのモデルが動かない。なんでだろう...
Go言語で並列プログラミング!! ~ Go注文は並列処理ですか? ~
街の国際バリスタデータサイエンティストになりたい!
Go言語とは
Go言語はGoogleのエンジニアが作った言語で、DockerやKubernetes, Vuls(脆弱性スキャナ)などに用いられている言語である。Go言語の持つ特徴の中でもっともカッコイイ特徴は並列プログラミングを楽に実装できるということだ。今日は久しぶりにGo言語を使って並列プログラミングをしよう。
題材
「コーヒーを挽いて淹れる」
といっても、コーヒーを挽いて淹れる過程を並列プログラミングでシミュレーションするだけなので、実際に作るわけではない。 手順を簡単に説明すると下記のとおり。
- ポットでお湯を沸かす
- コーヒー豆を挽く
- コーヒーを淹れる
どこが並列になるかというと、「お湯を沸かしている」ところと、「コーヒー豆を挽く」ところである。お湯が沸くのをただ待つのは時間の無駄なので,お湯が沸くのを待っている間にコーヒー豆を挽く。お湯を沸騰させて、コーヒー豆をミルで砕いたら、ドリッパーの上にコーヒーフィルタをいれて、粉になったコーヒーをコーヒーフィルタに入れる。そしてお湯を注ぐ。
作成したコード
コードは下記の通り。
package main import ( "fmt" "time" ) func main() { fmt.Println("美味しいコーヒーを淹れるぞい!!") bw := make(chan float64) // ポットのお湯が沸いたことを通知するチャネル water := 500.0 fmt.Printf("ポットに、%.1fmlの水を入れる\n", water) fmt.Println("ポットのスイッチをOnにする") go BoilWater(water, bw) gcb := make(chan float64) // コーヒー豆を挽いたことを通知するチャネル beans := 12.0 fmt.Printf("%.1fgのコーヒー豆をコーヒーミルの中に入れて、豆を挽く\n", beans) go GrindCoffeeBeans(beans, gcb) GetReady(bw, gcb) // コーヒ=を淹れる MakeCoffee(water) fmt.Println("コーヒーができました") return } func BoilWater(w float64, c chan float64) { // お湯を沸かします done := time.Tick(time.Duration(w/2) * time.Second) for { select { case <-done: fmt.Println("お湯が沸いたぜよ") // お湯が沸いたので通知 c <- w return default: fmt.Println("グツグツ!") time.Sleep(time.Duration(10) * time.Second) } } return } func GrindCoffeeBeans(beans float64, c chan float64) { estimateTime := beans * 3.0 // コーヒーミルで豆を挽きます done := time.Tick(time.Duration(estimateTime) * time.Second) for { select { case <-done: // コーヒー豆を挽き終えたので通知 c <- beans return default: fmt.Println("ゴリゴリ! バリバリ!") // コーヒーを挽く音 time.Sleep(time.Second) } } } func GetReady(bwc chan float64, gcb chan float64) { hotWater := 0.0 coffeeGrounds := 0.0 for { select { case hw := <-bwc: hotWater += hw fmt.Printf("熱湯: %.1fml 用意しました\n", hotWater) case g := <-gcb: coffeeGrounds += g fmt.Printf("コーヒーの粉: %.1fg 用意しました\n", coffeeGrounds) default: if hotWater > 0.0 && coffeeGrounds > 0.0 { fmt.Println("コーヒーフィルターの端を折ってドリッパーの上に載せる") fmt.Println("ドリップする準備が完了") return } } } } func MakeCoffee(hotWater float64) { fmt.Println("コーヒーをドリップします") // 泡が出る最大量 bubble := 1000.0 initBubble := bubble check := time.Tick(20 * time.Second) coffee := 0.0 for { select { case <-check: // 注ぐお湯の量 pour := 100.0 fmt.Println("もういいかい?") if bubble > initBubble*0.6 && hotWater > 0 { // お湯がまだあり、泡がまだ一定以上ある fmt.Println("まーだだよ") if hotWater >= pour { hotWater -= pour coffee += pour } else { coffee += hotWater hotWater = 0 } // 泡が減る bubble -= 100.0 } else { fmt.Println("もういいよ") time.After(20 * time.Second) fmt.Printf("コーヒー: %fmlできました。\n", coffee) return } } } return }
実行結果
実行結果は下記の通り。
$ go run main.go 美味しいコーヒーを淹れるぞい!! ポットに、500.0mlの水を入れる ポットのスイッチをOnにする 12.0gのコーヒー豆をコーヒーミルの中に入れて、豆を挽く ゴリゴリ! バリバリ! グツグツ! ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! グツグツ! ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! ~ 中略 ~ ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! ゴリゴリ! バリバリ! コーヒーの粉: 12.0g 用意しました グツグツ! グツグツ! グツグツ! ~ 中略 ~ グツグツ! グツグツ! グツグツ! グツグツ! お湯が沸いたぜよ 熱湯: 500.0ml 用意しました コーヒーフィルターの端を折ってドリッパーの上に載せる ドリップする準備が完了 コーヒーをドリップします もういいかい? まーだだよ もういいかい? まーだだよ もういいかい? まーだだよ もういいかい? まーだだよ もういいかい? もういいよ コーヒー: 400.000000mlできました。 コーヒーができました
解説
GrindCoffeeBeans関数でコーヒーミルでコーヒーを挽く音「ゴリゴリ!バリバリ!」を出すのと同時に、BoilWater関数でボイルでお湯を沸かす音「グツグツ!」が出ていることを確認できただろうか。BoilWater関数とGrindCoffeeBeans関数が同時に並列で動いているためこのような結果になる。
全体の流れを把握するためにmain関数に注目してほしい。
func main() { fmt.Println("美味しいコーヒーを淹れるぞい!!") bw := make(chan float64) // ポットのお湯が沸いたことを通知するチャネル water := 500.0 fmt.Printf("ポットに、%.1fmlの水を入れる\n", water) fmt.Println("ポットのスイッチをOnにする") go BoilWater(water, bw) gcb := make(chan float64) // コーヒー豆を挽いたことを通知するチャネル beans := 12.0 fmt.Printf("%.1fgのコーヒー豆をコーヒーミルの中に入れて、豆を挽く\n", beans) go GrindCoffeeBeans(beans, gcb) GetReady(bw, gcb) // コーヒ=を淹れる MakeCoffee(water) fmt.Println("コーヒーができました") return }
"go"がBoilWater関数とGrindCoffeeBeans関数の前に書いてあることが確認できただろうか。7行目ではBoilWater関数を実行するスレッドを作成してすぐに次の8行目を実行する。つまり、BoilWater関数の終了を待つことなく次の行を実行する。 しかし、このままだとBoilWater関数の実行が終わらないうちにreturnまで実行されてプログラムが終了する。それを防ぐためにGetReady関数の中の無限ループでBoilWater関数の終了を待ち受ける。GrindCoffeeBeans関数も同様である。
func GetReady(bw chan float64, gcb chan float64) { hotWater := 0.0 coffeeGrounds := 0.0 for { select { case hw := <-bw: hotWater += hw fmt.Printf("熱湯: %.1fml 用意しました\n", hotWater) case g := <-gcb: coffeeGrounds += g fmt.Printf("コーヒーの粉: %.1fg 用意しました\n", coffeeGrounds) default: if hotWater > 0.0 && coffeeGrounds > 0.0 { fmt.Println("コーヒーフィルターの端を折ってドリッパーの上に載せる") fmt.Println("ドリップする準備が完了") return } } } }
GetReady関数の無限ループについて
チャネルからの通知を待ち受ける部分についてここで説明する
for { select { case hw := <-bwc: hotWater += hw fmt.Printf("熱湯: %.1fml 用意しました\n", hotWater) case g := <-gcb: coffeeGrounds += g fmt.Printf("コーヒーの粉: %.1fg 用意しました\n", coffeeGrounds) default: if hotWater > 0.0 && coffeeGrounds > 0.0 { fmt.Println("コーヒーフィルターの端を折ってドリッパーの上に載せる") fmt.Println("ドリップする準備が完了") return } } }
GetReady関数内の無限ループのなかのselect文では、caseの後に書かれたチャネルbwcやgcbから値が受信されなければ、defalut文の中の
if hotWater > 0.0 && coffeeGrounds > 0.0 { fmt.Println("コーヒーフィルターの端を折ってドリッパーの上に載せる") fmt.Println("ドリップする準備が完了") return }
を実行し続けている。
チャネルから値を受け取った時
お湯が沸いたことを通知するためにチャネル(channel)を用いており、BoilWater関数内で
// お湯が沸いたので通知
c <- w
が実行されると、チャネルc(実態はチャネルbw)にwの値が入力される。
すると、GetReady関数の中のcaseが実行され、チャネルbwc(実態はチャネルbw)から変数hwに入力される。
case hw := <-bwc: hotWater += hw fmt.Printf("熱湯: %.1fml 用意しました\n", hotWater)
チャネルの挙動や並列処理を理解できただろうか。
感想
就活前にGo言語で通信を含むプログラムを書いて以来、久しぶりにGo言語を扱った。
Go言語で並列処理を実装するのは他の言語と比べてやっぱり楽だと思う。
コーヒーを作りたくなってきたので今日はここまで。
Proxyの影響下のUbuntu 18.04でJupyter Notebook with Keras on TensorFlow on Docker
就活が終わってまた研究を再開した。
いままで、研究用のサーバにCentOS7を使っていたが、研究でDeep Learningを使うことにしたので、NVIDIAのドライバが簡単にインストールできるUbuntu 18.04にOSを変えた。
Dockerfileを訂正したものを下記のリンクにのせました
20180804追記
NVIDIA driverのインストール
下記のコマンドで簡単にNVIDIAのGPUを扱うことのできるドライバをインストールできる。
$ sudo ubuntu-drivers autoinstall
ここまでは簡単だった。
しかし、2018/07時点ではまだNVIDIAのCUDAがUbuntu 18.04に対応しておらず、 強引な方法しかなさそうだった。
代替手段: NVIDIA Docker
簡単で、安心して利用できる方法がどこかにないか... どっかにないか、どっか、Docker... ということで、NVIDIA Dockerを利用することにしたw NVIDIA DockerはDockerのラッパーで、DockerコンテナからGPUを扱うことのできる優れものである。詳しくは下記のリンクを参照してください。
NVIDIA Container Runtime for Docker github.com
DockerコンテナのProxyの問題を回避する策: Docker Hub
これでうまくいくように思えたが、私の環境はProxy下であり、Docker自体は使える(docker pullはできる)が、Dockerコンテナの中から外部に通信ができず、docker build ができなかった。色々試したがどれもうまくいかなかった。
しょうがないのでDocker Hubにdocker imageを作成し、そのレポジトリからダウンロードすることにした。
GitHubにレポジトリを作成し、下記のようなDockerfileをそこに置いた。
FROM tensorflow/tensorflow:latest-gpu-py3 MAINTAINER geotaru RUN apt-get update -y && apt-get upgrade -y && \ apt-get install -y git \ build-essential \ wget \ curl \ graphviz RUN pip install keras \ numpy \ scikit-learn \ pandas \ scipy \ seaborn \ matplotlib \ plotly \ jupyter \ tqdm \ cython \ jupyter_contrib_nbextensions \ pydot \ graphviz \ pydot3 \ pydot-ng \ folium \ RISE RUN jupyter contrib nbextension install --user && \ mkdir -p $(jupyter --data-dir)/nbextensions && \ cd $(jupyter --data-dir)/nbextensions && \ git clone https://github.com/lambdalisue/jupyter-vim-binding vim_binding && \ jupyter nbextension enable vim_binding/vim_binding && \ jupyter-nbextension install rise --py --sys-prefix && \ jupyter-nbextension enable rise --py --sys-prefix WORKDIR /notebooks
Docker HubのAutomated buildという機能を利用して、GitHubのレポジトリとDocker Hubを連携させた。これにより、GitHubのレポジトリにpushするたびにGitHubのレポジトリのDockerfileの内容のDocker imageがDocker Hub上でbuildされる。
$ docker pull "Docker Hubのレポジトリ名"
でKeras on TensorFlow on Dockerな環境を構築することができた。
下記のレポジトリに作成したものを置きました。
https://hub.docker.com/r/geotaru/keras-notebook/
$ docker pull geotaru/keras-notebook
でダウンロードできます。
docker run --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=0 --rm -p 8888:8888 -v `pwd`:/notebooks -v `pwd`/data:/notebooks/data -it geotaru/keras-notebook
のようなコード(ご自分の環境に合わせて修正してください)を作成して、 http://localhost:8888/"ターミナルに出力されたtoken"にブラウザでアクセスすれば Keras on TensorFlow on GPUなJupyter notebookを使えます。
感想
現時点20180706でUbuntu 18.04でKeras + GPUの環境構築するのはまだめんどくさい。
ProxyやCUDAの問題が発生したせいで大げさになってしまった気がするが、これはこれで、環境を隔離し持ち運べるという点では良い解決策かなと思う。
Dockerのオーバーヘッドがどれだけあるかわからず、性能がどれだけ低下するか、私、気になります!
参照
How to install the NVIDIA drivers on Ubuntu 18.04 Bionic Beaver Linux linuxconfig.org
Ubuntu 18.04へのCUDAインストール方法 qiita.com
NVIDIA Container Runtime for Docker github.com
Docker Hub の自動構築 Docker Hub の自動構築 — Docker-docs-ja 17.06.Beta ドキュメント
バード電子の木製パームレスト買ってしまった
つい買ってしまった
つい手が滑ってというか、深夜のテンションでHHKBのための木製パームレストを買ってしまった。狭い机の上でもなんとかおけるくらいの大きさですごくよい。 底面に貼るグリップは2つのサイズから選べるが自分は高いほうを取り付けた。HHKBとの段差がなくなってなかなかよい。
アルバイト先に持って行って疲れ具合を試そうかな。
メモリが足りない場合に考えるべきこと
まえがき
私は研究で大きなデータセットを用いてプログラミングすることがよくある。
データのサイズが大きいとRAMの容量が32GBのサーバだとメモリが足りなくなる。そんな時に考えるべきことをまとめておく。
メモリが足りない場合の対処手順
- メモリを買う。スケールアウトできるように最初からプログラムを作成しているなら、マシンを買い足す。
- 予算などの都合で1が実行できない場合、プログラムを見直す。
1についてだが、最も簡単で小さいリスクでメモリ不足を解決できる。しかも早い。2についてだが、すでに動いているプログラムをいじるため、リスクがある。しかし、場合によっては少ないコストでメモリの削減が実現できるから、これも考えるべきである。
プログラムの見直し
精度の変更
精度を変更する。具体的には64bitの表現を32bit表現に変更する。Pythonのnumpyであれば、
import numpy as np array = np.arrray([1, 2, 3]) array32 = array.astype(np.float32)
のように変更できる。 これができるのは機械学習のように数値の厳密な精度を要求されない場合だ。しかも、これはリスクが小さく実現でき,かつ効果的だ。具体的には64bitから32bitに変更した場合は、単純に計算して消費するメモリが半分で済む。
メモリを無駄に確保していないか
例えば巨大な2次元配列を行ごとに処理する必要があるとする。このとき2次元配列を全てメモリに格納する必要はない。一行ずつ処理すればメモリ消費量は小さくすることができる。この方法はメモリ消費量を抑えることができるが、配列すべてをメモリに入れる場合より処理時間が増える場合があるため、よく考えてからプログラムを変更するべきである。
あとがき
メモリが足りないなら、メモリを増設するべきだ。
メモリが足りなくてもハードウェアでなんとかできるように最初からスケーラブルなフレームワーク(Spark)などを使ってプログラムを組むべきだ。プログラムの変更は時間を消費するし、プログラムが動かなくなるリスクもある。研究をしていてそう感じた。