不確定な世界

科学の話題を中心に、勉強したことや考えたことを残していきたいと思います

Google ColabのJuliaランタイムにおいて、Unicode記号のTab補完が効かない

個人的備忘録メモ。

Google ColabでJuliaが正式サポートされて久しいが、Unicode記号のTab補完が効かず困っている(2025年4月現在)。
ひとまず、テキストセルを使えばある程度似たことはできそう*1

テキストセルでUnicode記号を編集

既にイシューも上がっているみたいなので、そのうち解決するかもしれない。

*1:すべてではない。\xorなどはうまくいかなかった。

「詳解3次元点群処理」;金崎朝子・秋月秀一・千葉直也著 読書感想

本日紹介するのは、金崎朝子・秋月秀一・千葉直也著「詳解3次元点群処理」。
著者陣は全員ロボティクス方面の研究者。私にとってはほとんど馴染みのない分野だが、素人なりにSFアニメや科学番組でこの手の3次元データ処理のような場面を見る度にかっこいいという厨二的な憧れがあった。最近、私のような専門外の人間でも読みやすそうな本が出版されたので、ちょっとした冒険のつもりで読んでみた。

本書はコーディングがメインのソフトウェアの技術書であり、特にPythonのOpen3Dライブラリの入門書という側面が強い。センサなどハード面については紹介のみで、サンプルデータは全て公開データを用いているので、実際にセンサを持っている必要はない。また理論面の説明もそれほど多くはないのだが、門外漢の私には目新しい知識が多く、非常に勉強になった。理論面で特に印象に残ったのは以下の2つ。

回転行列、オイラー角、クォータニオンの関係

この3つの回転の表現方法について、私は知識としてそういうものがあるとは認識していた*1。しかしそれはあくまでも点の知識であり、なぜ似たような概念があるのか、何が違うのかを疑問に思ったことすらなかった。本書を読むことで、今まで点在していた知識が整理されてつながり、線や面になった感覚がある。

  • (3次元)回転行列の要素数3x3=9だが、回転という物理現象を表すための制約条件から、自由度は3しかない。つまり、回転行列は回転という操作を表すには冗長な表現である。
  • オイラー角は自由度3に対して3変数の表現。最もコンパクトで無駄はないが特異点があり、その付近では解が不安定になる。
  • クォータニオンは4変数で無駄が少なく、特異点もない。また、クォータニオンは球面線形補完が可能であり、アニメーションを扱うのに便利、というのが最大の利点である。

点群の法線ベクトルの求め方

点群の法線ベクトルを求めるには、ある注目点の近傍点を求め、近傍点群の3次元座標に対して主成分分析を行う。固有値が最も小さい軸(近傍点群の座標の分散が最も小さい、つまり「その方向に点群が平べったく分布している」軸)がその注目点の法線ベクトルになる。機械学習分野の次元削減の観点では、固有値が大きい(分散が大きい)方が情報量が多いと解釈するのだが、目的によっては分散が小さい方に利用価値があるという点が、私にとっては新鮮で、面白い!

Google Colabでの可視化

本書のサンプルコードでは可視化はopen3d.visualizationモジュールを用いて、GUI Windowで行っている。私はGoogle Colabでプログラムを動かすために、Plotlyを用いて可視化を行った。
open3d.visualization.draw_giometries()関数の代わりとしては、こちらのページのdraw_geometries()関数をベースに、以下のような関数を作成した。

import plotly.graph_objects as go
import open3d as o3d
import numpy as np

def draw_geometries(geometries, show=True):
    def cubes(size, pos_x, pos_y, pos_z, color):
        # https://community.plotly.com/t/plotly-graph-objects-volume-cube/75455/4
        x, y, z = np.meshgrid(
            np.linspace(pos_x-size/2, pos_x+size/2, 2),
            np.linspace(pos_y-size/2, pos_y+size/2, 2),
            np.linspace(pos_z-size/2, pos_z+size/2, 2),
        )
        x, y, z = x.flatten(), y.flatten(), z.flatten()
        R, G, B = color
        return go.Mesh3d(x=x, y=y, z=z, alphahull=1, flatshading=True, color=f"rgb({R},{G},{B})")

    graph_objects = []
    for geometry in geometries:
        geometry_type = geometry.get_geometry_type()

        if geometry_type == o3d.geometry.Geometry.Type.PointCloud:
            points = np.asarray(geometry.points)
            colors = None
            if geometry.has_colors():
                colors = np.asarray(geometry.colors)
            elif geometry.has_normals():
                colors = (0.5, 0.5, 0.5) + np.asarray(geometry.normals) * 0.5
            else:
                geometry.paint_uniform_color((1.0, 0.0, 0.0))
                colors = np.asarray(geometry.colors)

            scatter_3d = go.Scatter3d(x=points[:,0], y=points[:,1], z=points[:,2], mode='markers', marker=dict(size=1, color=colors))
            graph_objects.append(scatter_3d)

        if geometry_type == o3d.geometry.Geometry.Type.TriangleMesh:
            triangles = np.asarray(geometry.triangles)
            vertices = np.asarray(geometry.vertices)
            colors = None
            if geometry.has_triangle_normals():
                colors = (0.5, 0.5, 0.5) + np.asarray(geometry.triangle_normals) * 0.5
                colors = tuple(map(tuple, colors))
            elif geometry.has_vertex_colors(): # 第3章で追加
                colors = np.asarray(geometry.vertex_colors)
            else:
                colors = (1.0, 0.0, 0.0)

            mesh_3d = go.Mesh3d(x=vertices[:,0], y=vertices[:,1], z=vertices[:,2], i=triangles[:,0], j=triangles[:,1], k=triangles[:,2], facecolor=colors, opacity=0.50)
            graph_objects.append(mesh_3d)

        # 追加
        if geometry_type == o3d.geometry.Geometry.Type.LineSet:
            lines = np.asarray(geometry.lines)
            points = np.asarray(geometry.points)
            colors = None
            if geometry.has_colors():
                colors = (np.asarray(geometry.colors) * 255).astype(int)
            else:
                geometry.paint_uniform_color((0.0, 0.0, 0.0))
                colors = (np.asarray(geometry.colors) * 255).astype(int)

            for (i, j), (R, G, B) in zip(lines, colors):
                line_3d = go.Scatter3d(
                    x=points[[i, j], 0], y=points[[i, j], 1], z=points[[i, j], 2], mode="lines",
                    line=dict(width=1, color=f"rgb({R},{G},{B})")
                )
                graph_objects.append(line_3d)

        # 追加
        if geometry_type == o3d.geometry.Geometry.Type.VoxelGrid:
            centers = []
            colors = []
            has_colors = geometry.has_colors()
            for voxel in geometry.get_voxels():
                center = np.asarray(geometry.get_voxel_center_coordinate(voxel.grid_index))
                centers.append(center)
                if has_colors:
                    colors.append(np.asarray(voxel.color))
                else:
                    colors.append((0.0, 0.0, 0.0))
            centers = np.asarray(centers)
            colors = (np.asarray(colors) * 255).astype(np.uint8)
            voxel_size = geometry.voxel_size
            for center, color in zip(centers, colors):
                cube = cubes(voxel_size, center[0], center[1], center[2], color=color)
                graph_objects.append(cube)

    fig = go.Figure(
        data=graph_objects,
        layout=dict(
            scene=dict(
                xaxis=dict(visible=False),
                yaxis=dict(visible=False),
                zaxis=dict(visible=False)
            )
        )
    )
    if show:
        fig.show()
    else:
        return fig, graph_objects

また、第4章のためにPlotlyでアニメーションを表示する部分はこちらのページを参考にした。

# フレームの作成
pcds = reg.pcds
indices = reg.closest_indices
pcd_t = reg.pcd_t
line_list = [GetCorrespondenceLines(pcd_s, pcd_t, index) for pcd_s, index in zip(pcds, indices)]

frames = [
    go.Frame(
        data=draw_geometries([pcd_t, pcd_s, lines], show=False)[1],
        layout=dict(
            scene=dict(
                xaxis=dict(visible=False),
                yaxis=dict(visible=False),
                zaxis=dict(visible=False)
            )
        )
    )
    for pcd_s, lines in zip(pcds, line_list)
]
# アニメーションの表示
fig, _ = draw_geometries([pcd_t, pcds[0], line_list[0]], show=False)
fig.update_layout(
    title="ICP Animation",
    title_x=0.5,
    width=600,
    height=600,
    updatemenus=[
        dict(
            buttons=[
                dict(
                    args=[
                        None,
                        {
                              "frame": {"duration": 50, "redraw": True},
                              "fromcurrent": True,
                              "transition": {"duration": 0}
                        }
                    ],
                    label="Play",
                    method="animate"
                )
            ],
            type='buttons',
            showactive=False,
            y=1,
            x=1.8,
            xanchor='right',
            yanchor='top'
        )
    ]
)

fig.update(frames=frames)
fig.show()

最後に

自分にとって未知の分野であり、きちんと理解できたかは怪しいものの、久々に「新しいことを学んだ」という感触があった。この手の分野は環境構築が難しいイメージがあったが、本書のサンプルコードはPythonのみで完結し、Open3Dもpipで簡単にインストールできるので、専門外の人でも簡単に遊ぶことができる。3次元データをぐりぐり動かすだけでも面白いので、興味のある方がいたらぜひ読んでみてほしい。

*1:余談だが、回転行列に余分な1がついていることについて、学生時代に習ったときにはそういうものだと深く考えずに暗記に徹した。今回改めて回転行列を目にすると、パーセプトロンを実装するときにバイアス項も含めて行列積にまとめられるようにダミーで1をつけるのと全く同じことだと納得した

「大規模言語モデルは新たな知能か」;岡野原大輔著 読書感想

本日紹介するのは、岡野原大輔著「大規模言語モデルは新たな知能か」。
著者の岡野原氏は日本が誇る天才集団、Preferred Networksの共同創業者として著名である。学生時代には言語モデルを研究していたらしく、まさに大規模言語モデルを語るのにふさわしい人物だ。
本書は130ページ程度と薄めで、かつ説明レベルは一般向けである。しかしその情報密度は驚くほど濃い。特に後半の5章6章あたりの大規模言語モデルの仕組みに踏み込む部分は読んでいて情報に酔ってしまった。本書は、ChatGPTのハウツーからは一歩離れ、ストイックに大規模言語モデルの原理を考察したい人にお勧めである。

大規模言語モデルは事前にロールプレイを指示したり回答フォーマットを例示したりすることで振る舞いを制御できることはよく知られているが、その仕組みについてはずっと疑問に思っていた。
本書によると、注意機構の数式を変形すると、重みパラメータを一時的に変えているとみなすことができるそうだ。つまり、プロンプトによる指示や、自分自身が生成した応答に合わせて、モデルをその場で学習しているようなのだ。具体的には、注意機構は過去の文脈の予測誤差に応じた勾配降下法と同じ効果をシミュレーションして、次の単語を予測しているらしい。このような内容が、メタ学習や分布外汎化といったキーワードとともに説明されている。
なおこれに関連して一つ補足すると、注意機構はあくまでもフィードフォワード処理であるため、RNNのように本当の意味で過去の文脈を記憶する機能があるわけではない。GPTに記憶を持たせるには、プロンプトに加えてユーザーの入力と言語モデルの出力の履歴も入力しなければならない。つまり、言語モデル自体はフィードフォワード処理だが、チャットシステム全体でRNNに似た構造を作っていることになる。このあたりは自分でOpenAIのAPIを直接触ることで理解した。

また、LLMはオッカムの剃刀の原則に反してパラメータ数がそのまま性能に直結するというスケーリング則によって機械学習業過の常識を覆したことでも有名である。
大規模言語モデルが持つ高い汎化性能については、2つの仮説が紹介されている。まず宝くじ仮説は、ニューラルネットワークには初期値の時点でタスクに対して最適な部分ネットワークが隠れており、学習とはその部分ネットワークを掘り起こす作業であるというものだ。モデルが大きいほどそのような部分ネットワークが存在する確率が大きくなるため、モデルの規模が大きくなるほど性能が良くなるというのだ。この仮説は、「モデルの規模が大きくなるとある時点で突然タスクが上達する」という創発現象にも関わっているらしい。また、「平坦な最小解仮説」は、大きなモデルを勾配法で最適化すると、平坦な最小解という状態に到達する確率が高いというものだ。平坦な最小解とは、多少パラメータをずらしてもパフォーマンスが変わらないような解であり、この状態はモデルの複雑度が小さいことが理論的に分かっているらしい*1

これ以外には、トランスフォーマーの注意機構が短期記憶に、全結合ブロックが長期記憶に相当するのではないかという仮説が非常に示唆的で感銘を受けた。ここに書けるほど咀嚼できていないので、皆さんにはぜひ自分で読んでみて欲しい。

なお、ChatGPT登場以降、様々な勉強会や討論会が行われている。私は以下のような動画を見て勉強中である。
www.jdla.org
www.t.u-tokyo.ac.jp

また、「JDLA Generative AI Test 2023」も受験し、無事合格している。

ChatGPTが登場して一年がたった。オープンソースLLMやLangChainなどのライブラリの整備が進み、今や生成AI戦国時代となっている。私自身は生成AIに関してはユーザーの立場ではあるが、今後も世の中に置いて行かれないよう、AIの利活用や情報収集、勉強を続けていきたい。

*1:本書とは関係ないが、量子アニーリングで得られる解はこの「平坦な最小解」の性質を持っているようだ

「つくりながら学ぶ!PyTorchによる発展ディープラーニング」;小川雄太郎著 読書感想

本日紹介するのは、小川雄太郎著「つくりながら学ぶ!PyTorchによる発展ディープラーニング」。
書名に「発展」とあるように、本書で扱われる手法は非常に発展的・応用的だ。物体検出のSSD、姿勢推定のOpenPose、異常検知のAnoGANなどは、メディアや書籍などで一度は聞いたことがあるだろう。しかしこの手のモデルは構造が複雑で、メディアの解説はもちろん原著論文を読んでもいまいち理解できないことも多い。このような論文レベルの手法*1の仕組みが詳細に解説されているだけでもありがたいのだが、本書はさらに、これらのモデルをPyTorchを使って1から組み上げていく*2。既にライブラリがある程度整備されているTransformerやBERTも、自分で実装する経験は貴重なものになった。なお本書の想定開発環境はAWSだが、私はGoogle Colaboratory(以下colab)で実装した。colab環境ではデータのダウンロードや解凍・モデルの学習にかかる時間が書籍に書いてあるより数倍速く、colabがいかに強力であるかも再認識することができた*3

また、サポートが丁寧であることも本書の特徴の一つである。まずコード中のコメントが非常に充実している*4。特に実装が非直感的になっている部分は徹底的にコメントで解説してくれているので、迷子にならずに済む。githubのイシューも活発で、著者が直々に議論に参加している。サンプルコード付き書籍のgithubはせいぜいこっそりバグフィックスしている程度のものが多いが、ここまで丁寧にアフターサービスを実施している著者には本当に頭が下がる。

最後にもう一つ。本書は基本的にコーディング中心で理論の説明はあまりないのだが、所々に著者独自の考察(感覚的理解)が述べられており、それが非常に参考になる。ネタバレになるので詳細は控えるが、DCGANにLeakyReLUが使われる理由や、CBOWよりもskip-gramで得た分散表現の方が性能が良い理由*5についての考察がある。興味のある方はぜひ本書を読んでみて欲しい。

こちらのイシューによると、著者は業界の動向を見て、方向性がある程度FIXしたらアルゴリズムを選定し直して新刊を出す予定のようだ。その時にどのアルゴリズムが選定されるかは分からないが、本書のような粒度でViTや拡散モデルなどを解説していただけるのであれば、その新刊も名著になることは確定だろう。楽しみにしたい。

TIPS+トラブルシューティング

本書は既に出版されてだいぶ経っており、所々エラーが発生することもある。前述したのように本書のgithubイシューは非常に活発で、大抵のエラーはイシューを調べればすぐに解決する。しかし、それでもやはり一筋縄ではいかなかった部分もあり、ここでは特に苦労した点やcolab特有と思われるエラーを挙げる。

公開GoogleDriveや公開Dropboxからcolabに直接ダウンロードする

本書の学習データや学習済みモデルの一部はGoogleDriveやDropboxからダウンロードすることになっている。手動でダウンロードしてローカルドライブからcolab環境に持ってきても良いのだが、本書に用いるデータはサイズが大きく、ドライブの容量を圧迫する。それにマウントは認証の手間があるので、直接ダウンロードした方が手っ取り早いと思う。GoogleDriveについてはこちらこちらを参考にして、以下のようなコードを使用した*6。もちろん、URLやファイル名の部分はその都度変更が必要。

!pip install --upgrade gdown
import gdown
gdown.download("https://drive.google.com/u/0/uc?id=0ByFQ96A4DgSPUm9wVWRLdm5qbmc", "./data/vector_neologd.zip", quiet=False)

また、Dropboxからのダウンロードについてはこちらを参考にした。例えば、以下のようになる。

#!wget -O tmp/FILENAME "対象URL(末尾?dl=1)"
!wget -O ./data/COCO.json https://www.dropbox.com/s/0sj2q24hipiiq5t/COCO.json?dl=1
!wget -O ./data/mask.tar.gz https://www.dropbox.com/s/bd9ty7b4fqd5ebf/mask.tar.gz?dl=1

SSDの推論時のエラー

対応するイシューはこちら。一番簡単な対策であるダウングレードについては、古いバージョンがpipの対象外?になっていたため実行できなかった。そのためryota0051さんの5/10のコメントを参考にしてスクリプトを修正したところ、無事に動いた(修正したスクリプトは%%writefileコマンドで保存するのが一番手っ取り早いだろう。colabセルとしてそのまま実行してもいいかもしれないが未確認)。

姿勢推定(OpenPose)の学習でメモリオーバー

バッチサイズを32から24にしたら動いた。たまたまその時だけcolabのGPUガチャに外れただけかも。

NEologd辞書のインストール

こちらの記事を参考にしつつ、さらにgit curl python-mecabという記述を削除。以下のようになる。

!apt-get -q -y install sudo file mecab libmecab-dev mecab-ipadic-utf8 > /dev/null #git curl python-mecab > /dev/null
!git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git > /dev/null 
!echo yes | mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n > /dev/null 2>&1
!pip install mecab-python3 unidic-lite > /dev/null
!pip install janome > /dev/null

# シンボリックリンクによるエラー回避
!ln -s /etc/mecabrc /usr/local/etc/mecabrc

Mecabの-Ochasenオプションを単独で使うとエラー

以下のように-dオプションで辞書を明示的に指定。

# m_t = MeCab.Tagger("-Ochasen")
m_t = MeCab.Tagger("-d /var/lib/mecab/dic/ipadic-utf8 -Ochasen")

torchtextのFieldやTabularDatasetの廃止

現在は既にtorchtext.legacyすら廃止されているので、おとなしくv0.8.1までダウングレードした。これもpipで対応できなくなると更なる対策が必要かも。

utils.dataloader.get_IMDb_DataLoaders_and_TEXT関数がエンコーディングエラー

colabセルにコピペしたら普通に動いた。また、io.open関数にencoding="utf-8"を追加して%%writefileコマンドでファイルを保存し直したら動いた。よくわからない。

youtube動画のダウンロードと前処理

1. 幸いなことに必要な動画はたった8つなので、ここは手動でダウンロード。ファイル名は1.mp4、2.mp4 ... などと通し番号を付けた。これをzipにまとめてcolab環境にアップロード。パスが/tmp/kinetics/*.mp4になるように展開する。

2. download.pyを以下のように修正してdownload2.pyとして保存(%%writefileコマンド使用)。以下は修正箇所しか見せないが本書を読んでいる人なら分かると思う。

#import youtube_dl
#中略 
###追加
def filename_generator():
    for i in range(1, 1000):
        yield str(i) + ".mp4"
g = filename_generator()
###
def download_clip(video_identifier, output_filename,
    #中略
    #tmp_filename = os.path.join(tmp_dir,
    #                            '%s.%%(ext)s' % uuid.uuid4())
    #追加
    tmp_filename = os.path.join(tmp_dir, next(g))
    command = ['youtube-dl',
    #中略
    while True:
        try:
            #output = subprocess.check_output(command, shell=True,
            #                                 stderr=subprocess.STDOUT)
            #追加
            pass

なお、ffmpegはcolab環境のものがきちんと動いたので修正箇所はyoube-dlに関する部分だけでよかった。

3. 以下のコマンドでdownload2.pyを実行。注意点は、上記の修正でジェネレータを使っており並列処理に対応していないため、ジョブ数を1としていること。それでも大した処理時間ではなかった。

!python ./video_download/download2.py -n 1 ./video_download/kinetics-400_val_8videos.csv ./data/kinetics_videos/

これ以降の処理は問題なく動いた。

*1:もはや"最先端"ではないが。

*2:フレームワークを使っているのでゼロからとは言わない笑

*3:もともと学習に極端に時間がかかる章では学習済みモデルがそのまま使えるようになっている

*4:本書の分厚さの要因の一つである

*5:単純にskip-gramの方が難しいタスクだからというのはよく聞くが、著者はもう一つ理由を挙げており、そちらが目からウロコだった。

*6:2023年2月現在、ダウングレードが必要だった

E資格2022#1合格

2022年2月19日(土)に受験したJDLA Deep Learning for ENGINEER(通称E資格)に無事合格した。

f:id:quanta087:20220310201310p:plain

分野別得点率

応用数学:100 %
機械学習:89 %
深層学習:96 %
開発環境:100 %
機械学習の数字を見て一瞬「そんなにミスったっけ?」とびっくりしたが、そもそも出題数が少ないためだと気が付いて一安心。おそらく24/27といったところだろう。深層学習も出題数を考えると2~3問ミスったことになる。1問は試験が終わってすぐに間違いに気が付いたが、残りはどこだろう…?気になる。


以下、受験体験記的もの。

受験のモチベーション

私はこの数年間、ディープラーニング機械学習について書籍を中心に独学で学んできた(その中のいくつかはこのブログで読書感想記事を書いている)。E資格を受験する目的は人それぞれだが、私の場合、自分が独学してきた知識が世間に通用するかどうかを確かめたかったことが大きな理由である。結果的にそれなりの高得点で合格したということで、この目的は肯定的に達成することができたと思う。

認定講座

E資格の受験には、事前にJDLA公認の機関から講座を受講する必要がある。私はスキルアップAI社「現場で使えるディープラーニング基礎講座」のオンライン講座(5万円)のプランを受講した。数十万円の講座もある中では格安と言っていい値段だが、その分サポートは薄いのである程度の前提知識や独学力は必要になる。私はそこら辺は全く心配していなかったので、迷わずこの講座を選択した。
講座の内容も分かりやすく、特にこれまで知識が手薄だったRNN系や自然言語処理周りへの抵抗感がなくなったのは間違いなく本講座のおかげである。LSTMの計算グラフなんて本を読むだけなら絶対スキップしていた。また、修了課題の一つである「手書き文字認識モデルの作成」も手ごたえがあった。テストデータ正解率0.975当たりまでは簡単に到達したがそこからかなり試行錯誤が必要になる絶妙な難易度だった。もちろん最終的にはクリアしたのだが、その際に壁を突破する決定打となったのが「よくお勧めされているけど今まで自分では使ったことがなかった、ある手法」だった。なるほどこれは強力だ、と自分の肌感覚として納得することができ、いい経験だった。

なお、スキルアップAIはE資格模試も提供しており(ただし、「現場で使えるディープラーニング基礎講座」受講者限定である)、私はこちらも受講した。年明けに受験し一発で合格点を取ったことで勉強の手ごたえを感じ、だいぶ心に余裕ができた。私が今回E資格の勉強に使用した教材の中で、最も本番のレベル感に近かったのはこの模試である。少々高額ではあるが、予算に余裕があるならぜひ受けた方が良い。

勉強に使用した書籍

上述した認定講座以外に、E資格対策として読んだ本は主に次の2冊。

ゼロから作るDeep Learning

グッドフェロー本はあまりにも学術的過ぎるので、事実上本書がE資格の公式テキストと言ってもいいだろう。これを機に再読した。かつて読んだときより明らかに知識の解像度が上がっており、特にかつてブラックボックスだったim2colが理解できるようになったことがうれしかった。

徹底攻略ディープラーニングE資格エンジニア問題集 第2版

通称黒本。過去問が公開されていないE資格において唯一の市販問題集。たとえE資格を受けなくても、「演習問題をこなすことで理解を深める」という勉強スタイルが好きな人にはお勧めの一冊である。掲載されている問題の中にはやや難しいものもある*1が、本書を十分にこなしていれば試験には受かるだろう。なお本書には模擬試験1回分も付いているが、本番のレベル感に対してかなり優しめなので、これが解けたからといって「なんだ、こんなもんか」と油断しないように注意してほしい。

その他、副読本として次の2冊をさっと読んだ。

ゼロから作るDeep Learning ❷ ―自然言語処理

こちらもE資格対策のバイブルとして紹介されることが多い。私も本当は写経しながらじっくり読みたかったが、ちょうど仕事が忙しくなりあまり本格的に取り組むことができなかった。自然言語処理関係についてはスキルアップAIの教材をメインに勉強した。

深層学習

いわゆるグッドフェロー本。一応E資格の公式テキストなので目は通した。色々示唆的なことが書いてあり考察(妄想)しながら読む分には面白いが、”E資格対策”としてはあまりにも重すぎる。純粋に学問的興味がある人が読めばいいと思う。

勉強期間

E資格のための勉強期間は2021年11月18日から始めてちょうど3か月。おおざっぱに分けると、最初の1か月で認定講座を修了、次の1か月で黒本や模試、スキルアップAIの知識テストなどを繰り返し解き(この期間がいわゆる”資格の勉強”感が一番強かった)、最後の1か月は上述した本を読んだりして知識を体系的に整理する、という配分だった。
なお、この勉強期間や読んだ本というのはあくまでも「E資格対策として」であり、通算では他にも技術書・理論書・一般向け啓蒙書や新書、あるいは哲学寄りの本や脳科学などの周辺分野の本といった、それなりの量の本を読んで勉強してきた。そのような前提知識があってこその合格なので、軽々しく「3か月で受かる」などと言うつもりはない。これからE資格を受験する皆さんは、自分のペースで頑張っていただきたい。

試験本番の感想

出題内容については規約のため詳しく書くことはできないが、まさに解いている最中に「ああ、なるほど!面白いなぁ」と思えるような問題もあり、学びのある非常に質の良い試験だった。120分で100題以上の問題を解くことになるので時間が心配になる人もいると思うが、過剰な心配はいらない。確かに平均すると1問1分の計算になるが、実際には即答できる問題も多いので、多少計算問題で躓いたり*2、論文出典の難問をじっくり考えたりしても、時間的にはかなり余裕がある。さらにCBTでは本を手で押えたり解答をメモしたりする手間がないので、想定よりもサクサク進むはずだ。私の場合、一通り解き終わった時点で残り時間がまだ53分あったので、見直しの時間を十分に確保することができた。

最後に

資格なんて役に立たないという人もいるが、E資格はそもそも免許的な性格の資格ではなく、知識を更新し続けることが前提で、あくまでも勉強の指針である。2022#2からはシラバスが変更になるらしいので、私もさっそく知識の更新が必要そうだ。最近は安価な認定講座もあり、E資格受験のハードルもだいぶ下がってきている。気になっている人はぜひチャレンジしてほしい。

*1:AlphaGOの問題とか初見で解ける人がいるのだろうか?

*2:なお私は本番で、計算結果が選択肢に無く焦るという展開が2回あった

「ゼロからつくるPython機械学習プログラミング入門」;八谷大岳著 読書感想

本日紹介するのは、八谷大岳著「ゼロからつくるPython機械学習プログラミング入門」。
私はこれまで機械学習の本を何冊か読んできたが、古典的な機械学習に関する入門書は、実質的にscikit-learnのチュートリアルで終わっていることも多い。機械学習の中身をもう少し踏み込んで理解したいと思い、本書を読んでみた。

書名からはコーディング中心の本という印象を受けるが、本書の性格はどちらかと言えば「サンプルコード付きの理論書」である。「理屈はともかく写経しろ」という内容を想定していると面食らうことになるが、そもそも昔ながらの機械学習は深層学習以上に数学そのものであり、その中身を理解するうえで数学は避けて通れない。一方、理論さえ理解してしまえば、アルゴリズムのコアはたった数行程度のコードでスクラッチ実装できてしまうということが、本書を読めば分かる。本書では純理論の本を読んでいるとありがちな、式の意図やイメージの掴みづらさに悩まされることがない。本文中の数式に沿ったコードが掲載されているので、例えばテンソルの形状だとか総和の対象(軸)などもコードを確認すれば誤解なく理解することができる。

章ごとの感想

第3章

まず勉強になったのは第3章の数学の復習。本書では特に最適化数学の解説に力が入っており、ラグランジュの未定乗数法が機械学習アルゴリズムのベースにあることが理解できた。私は最適化数学になじみがなく、特にベクトルの内積をベクトルで微分するような表記は苦手意識を持っていたので、本書で多少なりともこの分野に慣れることができたのは良かったと思う。本書を読む中で、この章は何度も読み返すことになった。

第4章

回帰アルゴリズムとして線形回帰とロジスティック回帰を学ぶ。線形回帰について、本の方針によっては単回帰(y=ax+b)に限定して係数を表す式を直接導く場合も多いが、本書のように線形代数に基づく表記では、重回帰に拡張したときに式に一切変更がないという圧倒的メリットがあることに気が付いた。また、ロジスティック回帰が実質的に分類モデルであるにも関わらず回帰と呼ばれるのは、交差エントロピー誤差の最小化がロジットを回帰していることに対応するため、という記述には目からウロコが落ちた。

第5章、第6章

分類モデルの章では、線形判別分析(LDA)、サポートベクトルマシン、ナイーブベイズ、決定木が扱われている。この章では特に、サポートベクトルマシンの理論から逃げていないことに好感を持った。ある程度のレベルの数式を扱っている本でも、サポートベクトルマシンの理論については「難解すぎる」としてscikit-learnやLIBSVMといったライブラリの使い方を説明するだけで終わってしまう場合が多い。本書では第6章のカーネルトリック*1まで含めて、サポートベクトルマシンの数式&スクラッチ実装を学ぶことができる(ただし、ここだけは行列の計算にcvxoptライブラリを用いている)。

第7章、第8章

 第7章はニューラルネットワーク、第8章は強化学習がテーマである。ニューラルネットワークについては他書で十分に学んでいるという人は、ここは飛ばしてもいいだろう。ただ、Adamをきちんと実装している点は参考になるかもしれない。強化学習に関しては、ページ数も少ないし、本書全体のテーマからはやや外れている印象を受けた。同じ機械学習スタートアップシリーズから強化学習も出ているようなので、強化学習に興味があるならそちらを読んだ方がいいだろう。

第9章

教師なし学習アルゴリズムとして、主成分分析と因子分析、k-means法が扱われている。PCAは分散を最大化する軸を見つけると説明されることが多いが、その根底には残差の二乗和の最小化があるという意味で、線形回帰に似ているということが理解できた。k-means法はアルゴリズムの概念はわかりやすいが実装は面倒くさそうという印象を持っていたが、重心からデータ点の距離を計算→データ点をクラスタに割り当て→クラスタの平均値を重心に再設定の流れがあまりに簡潔で驚いた。特に、カーネル法のところでも使われていた、np.tile関数を用いてデータの全ペアの計算を一度の行列演算で行うトリックは自分では絶対に思いつきそうにない。応用範囲が広そうなので、ぜひ自分のレパートリーに加えたいテクニックである。

まとめ

ここまで紹介したように、本書はかなり数式が多い本なので、想定読者は大学生や研究者、または研究寄りのエンジニアになるだろう。アルゴリズムの中身をきちんと勉強したいが、純理論の本にはハードルを感じるという人にお勧めである。機械学習の仕組みはもちろんであるが、もう少し一般化して、理論や数式をプログラムに実装するということも学ぶことができる。

全体として、非常に読み応えのある良い本であるが、一つ不満点を挙げるとすると、コードが書籍中で完結しないことだろうか。もちろんアルゴリズムのコア部分のコードは載っているのだが、グラフのプロットや性能評価などの補助的なコードは書籍中には載っていない。本書はかなり分厚いので、これ以上コードを載せることは現実的ではないのかもしれないが、「ゼロからつくる」と銘打っている以上は、もう少し読者に自分でコードを打ち込ませるように誘導する構成の方が良かったのではないかと感じた*2

*1:まったくの余談だが、データを写像することでモデルが非線形に対応するという話は、量子力学において状態ベクトルを回転させてから測定するのは測定器を回転させるのと同じ、という議論を連想した。

*2:例えばオライリー本はそういう思想で書かれていることが多いと思う

「量子コンピュータが本当にわかる!」;武田俊太郎著 読書感想

本日紹介するのは、武田俊太郎著「量子コンピュータが本当にわかる!」。
著者の武田先生は量子テレポーテーションで有名な古澤研のOBで、最近独立した研究室を立ち上げた若手研究者である。以前紹介した藤井先生が理論の専門家であるのに対し、武田先生は光を用いた実験(コンピュータの文脈ではハードウェア)の専門家だ。理論家と実験家、読み比べてみると面白い。

本書では波の重ね合わせと干渉によって計算を行う量子コンピュータの概念を、二重スリット実験のアナロジーを用いて一貫したイメージで説明している。その点だけでも分かりやすくておすすめなのだが、本書の見どころは何と言っても第5章「量子コンピュータの実現方法」と第6章「光量子コンピュータ開発現場の最前線」である。
第5章では、超伝導回路方式、イオン方式、半導体方式、光方式の量子コンピュータについて解説されている。使われている模式図は一般書としては比較的詳しく描かれていて、モノを重視する実験家らしさを感じた。特に光方式については第6章で実際の実験装置の写真とともに詳述されている。研究室の見取り図まで載っているのには笑ってしまったが、この手の本で著者自身の独自の研究成果を知ることができるのは貴重である*1

また、第3章は量子ビット量子論理回路の話なのだが、ここで一つ気付いたことがある。本書ではいわゆる制御NOTゲートのことを「量子版XOR」と説明しているのだ。制御NOTを実験物理学の観点から見ると、二量子間の相互作用により生じるエネルギー準位の微細構造により「制御ビットが特定の状態の時だけターゲットビットが電磁波に対して反応(共鳴)して状態が反転する」という操作である*2。制御NOTとはまさにその言葉の通りの操作であり、私はこれを専門用語としてそのまま受け入れていた。恥ずかしながら、この操作が数式的にはXORに相当すること自体は認識していたにもかかわらず「普通のコンピュータの論理回路と対比させて量子版XORということができる」という点にまでは気が回っていなかったのだ。些細なことではあるが、個人的には盲点、目から鱗であった。

最後に全体を通して感想だが、やはり実際に装置を組み立ててデータを取っている人が書く内容というものは説得力がある。写真も豊富で、概念的なたとえ話やポンチ絵だけではなくモノがないと納得できない人には本書は非常にお勧めできる。一方、本書の解説があまりにも優しすぎて、すんなり読み終わってしまった感も否めない。科学の本としての知的興奮度に関しては、藤井先生の方に軍配を上げたい。

量子コンピュータの計算部分は現在は超電導方式が主流だが、通信をしようとすると光を使わざるを得ないだろうから、武田先生の研究の重要性は今後さらに増してくるだろう。今後の発展が楽しみである。

*1:個人的には、実験データや信号の波形なんかが載っていると嬉しいのだが。もちろんデータは大切な知的財産だし、この手の分野の実験データは重力波の波形のように見た目だけで簡単に解釈できるものでもないのも理解できる。が、やはりラビ振動くらいは載せてあると、量子ビットを制御しているという説得力が増すと思うのだが。

*2:制御ビットが0と1の重ね合わせ状態だった場合、制御NOTによって「制御ビットが0のためターゲットが反応しなかった状態」と「制御ビットが1のためターゲットが反応した状態」の重ね合わせが生じる。これが量子もつれである。