ぱたへね

はてなダイアリーはrustの色分けができないのでこっちに来た

実践プロパティベーステスト

実践プロパティベーステストをさっと読んでみた。

『実践プロパティベーステスト ― PropErとErlang/Elixirではじめよう』www.lambdanote.com

この感想を読んでみて、実際のテストのイメージがわかない(だけどテスト全般には興味がある)人は対象読者です。買いましょう。

最初の方を読んでの感想

結局流し読みになってしまったのですが、せっかくなので感想を書きます。 流し読みになった理由の一つはWindowsでのErlangの動かし方が分からなかった事。Eralng自体はインストールできたのだけど、最初に出てくるコマンドrebar3から見つからなくて諦めてしまった。もう一つは、意外にも知ってることが多かったから。

プロパティベーステストとは、特定の処理に対して絶対守られるルールをプロパティとして記述し、それに違反しないかどうかをテストする手法です。本書によると「どのような入力値を与えても常に同じであるような振る舞い」をコードとして書くとプロパティになります。ここだけ読むと、静的なテストのように感じますが中身はランダム検証です。

最初の方を読んだ感想。 「おつりの合計額は、常に払った金額から請求額を引いた額に等しくなる」というプロパティについて、intで表せるようなプロパティは普通にassert入れれば十分な気がする。Erlangもそうだけど、型に厳しい言語であればコンパイラの型チェック通った時点である程度のチェックは通ってる。少なくともお釣りの金額を期待するところで顧客名のリストが返ってくるようなことはない。そういうのも期待して型も設計しているはず。

もうちょっと抽象的なリストのソートみたいな奴はどうテストするんだろうと思って読んでいくと、同じ関数を別の人が実装すると書いてあって、この辺で半導体の検証手法を思い出しました。

半導体の検証手法

検証手法に関しては、半導体の検証はロジカルな部分だけでもお金が突っ込まれていて実践投入されてます。例えば、Fomalityといった静的検証ツールがあって、大元のソースコードの修正と、別のフェーズで入れるパッチを入れた結果が、論理的に等価ですよねみたいな静的なチェックをやってくれます。

SystemCとかSVA(SystemVerilog Assertion)等、本番の半導体で動く記述よりも抽象度の高い(開発効率が高い)記述で、同じ挙動を別々に書くこと+ランダム検証で本書のプロパティチェックと同等の事が実施されています。ある条件を満たしてから、2CLK以内にこの状態にならないとエラーのような記述も普通に使います。これはステートに対するプロパティに相当します。ステートのassertionもand, orで簡単に組み合わせて複雑なシーケンスも記述可能になっています。

複雑なステートが入ってくるとそのプロパティが正しいのかどうかも問題になってきて、PCIとかUSB等の規格物については規格にあっているかどうかをチェックする専門家が作ったライブラリ(assertionの塊)が有償で売られてます。これも普通に使われていると思います。

本書でも取り上げられてますが、普通にランダム検証を使うと乱数が偏らないのでエッジケースをつきにくく、制約付きランダムで乱数を偏らせてエッジを狙ったり実動作に近づけたりします。例えばメモリアクセスに必要な時間を正確に見積もることができないので、アクセスのタイミングをランダムにしてシミュレーションするというのはどこでもやってると思います。

プログラマーのためのCPU入門 ― CPUは如何にしてソフトウェアを高速に実行するかwww.lambdanote.com

ラムダノートさんのプログラマーのためのCPU入門にを読むと、最近のCPUがどのタイミングでメモリにアクセスするかを事前に予測する難しさが分かります。

Encode->Decodeの対称プロパティは、僕が関係した画像系のプロジェクトでは非可逆の事が多く使えるケースは無かった気がします。

こう書くと半導体検証は恵まれているような気がしますが辛い話もいっぱいあります・・・

プロパティベースの検証に興味を持ったら、半導体の検証手法を調べてみると面白いです。普通に探すとデバイス(半導体そのもの)の検証やテストが出てくるので、Logic検証とかSystem検証で調べてみると良いです。

プロパティベースの使い道

本を読んでみて、C++で自分のプロジェクトに使えないかなと思ったところです。

Design Patternの実装のチェック

Strategyパターンのような複数の実装を同じように扱うようなライブラリを作ったとき、どの実装が来ても共通の振る舞いをプロパティにしたい。

移植ライブラリの等価チェック

今風の課題ですが、PyTorchの関数と同じ動きを関数をC++で作らないといけないとき、正解値をPyTorchで作って自作の関数を検証する。ただのランダム検証といわれたらそれまでか。

やはり、今の僕には文法や検証の概念よりは上手い使い方が知りたい。特に小さいプロジェクトでも、上手く使ってコスパ良い検証ができる手法を知りたいですね。

この後読みたいのは、n月刊ラムダノートの検証特集かな。大規模検証事例から個人プロジェクトまで。もしくは半導体で使われる検証手法(でソフトウェア設計にも応用が利きそうな物)の特集を、現役引退した半導体検証エンジニアの方々が書いてくれないかなと期待しています。

コンフィグレーション空間での可動範囲

コンフィグレーション空間での可動範囲

ロボティクスの勉強、3周目くらいに入ってます。

ロボットを可動範囲内でどう制御するかの話で、xyz空間での制約ではなくコンフィグレーション空間での制約を使う方法があります。

Modern Robotics: Mechanics, Planning, and Controlから

2軸のロボットの場合、姿勢としてθ1とθ2があります。そのθで可動範囲を決めることができれば、その中でpath planningをすれば、障害物にぶつかることなくロボを制御できます。左の図のxyで問題を解かなくても、右側のθ1、θ2で問題を解けばOK。

ロボの制御プログラムとしては、右側で解けるのは非常に魅力的なのですが、じゃ右の図はどうやって作るの?というのがずっと分かりませんでした。

Principles of Robot Motion: Theory, Algorithms, and Implementations に右の図の作り方が載ってました。まず、θ1とθ2が決まれば、ロボの姿勢が一意に決まります。障害物が既知であれば、その姿勢が衝突するかどうかが分かる。後は、θ1、θ2をgridに区切って、全領域でぶつかる、ぶつからないをプロットすれば右の図ができる。なるほど。

柱とか床とか絶対に動かない障害物に対しては有効な気がします。

コンフィグレーション空間からxyz空間の変換って何回か行列かけたり足したりするだけだから、今の姿勢から一定の範囲のコンフィグレーション空間についてGPU使って一気にベクトル演算して、リアルタイムに点群と衝突判定するとかできないのかな。

ソフトウェア設計のトレードオフと誤り

ソフトウェア設計のトレードオフと誤りを読みました。

www.oreilly.co.jp

業務用のソフトウェアを設計するときに検討すべき事をコードと共に説明してあります。立ち位置が業務用のソフトウェアで一貫しており、SLA(Service Level Agreement)やメンテナンスという言葉が目立ちます。SLA無しではいろんな事決まらないよねというスタンスはあまり他の本ではみないので貴重な本です。

ソースコードはJavaでライブラリ等もJava関係が多くです。Web系の開発メインで書かれていることもあり、僕の仕事とは7割くらいは関係無いのですが残りの3割がとても面白く読めました。

ただ、関係のない7割の部分のように経験していないところはあまり頭に入ってこず、経験しているところはまあそうだよねとなるので、これを読んで一気に視野が広がるかどうかは業務と自分の立ち位置によるかなと思います。人は経験から学ぶことが大半だなと感じました。

仕事する上で関係無いなと思った箇所は、データベースとメモリ、APIの同期非同期、エラー処理、分散システムと一貫性、ネットワークがからむパフォーマンス等です。本来の想定読者はここだと思う。

面白かったところを紹介していきます。

継承とコンポジションのトレードオフ

本の最初にGoFのデザインパターンが出てきたのが30年前という話から入り、デザインパターンがの整理が始まります。その中で継承とコンポジションのトレードオフについても説明があります。Javaのソースコードを例に課題と解決策が紹介されていて、その瞬間の課題が解決できるかどうかよりも、間違った継承を使う事で一般化されたクラスがそうでなくなったり、継承をした目的からずれているぞといった指摘があります。筆者の立ち位置がこれからメンテし続けるコードだぞって所が面白いです。

僕は継承とコンポジションだとコンポジションの方が良いと思っていて、「継承のために設計し、文書化する。そうでなければ継承を禁止する」の言葉通り、継承して使われるなら最初からそう設計すべきだと思う派です。(C++限定で。Pythonだと雑に継承する)

日付

ここは仕事と関係無いところだったが読み物として面白かったです。 いくつか紹介

  • 国内に置いて年齢の解釈は法律で決まっているが、道路交通法は別の解釈をしている
  • 2035年以降100年はうるう秒が廃止される
  • 日付を扱うアプリケーションのチェックリストの最初が「あなたのアプリケーションは相対性理論を扱う必要がありますか?」
  • 1月31日に一ヶ月を足して一ヶ月を引いたとき、1月31日に戻るライブラリは無い

Version管理

適当につけがちなバージョン番号にちゃんと意味を持たせる話が良かった。1.12.3-betaみたいな表記のそれぞれと、互換性との関係を文章化できていて、これは早速仕事に導入したい。初めて聞く話では無いんですが、自社のバージョン管理で悩んでいるとよく考えられているなと感じました。

シリアライズ

ここは仕事ではまりかけたところ。C++の列挙体をそのままシリアライズすると、列挙体が変わった時お手上げになる。面倒でも文字列にした方が良い。これと同じ事をProtcol Buffersを使って説明してあります。後から追加になっても、互換性を保つための工夫も書いてありました。

ただ、こういうのは一回はまると気がつくけど、はまる前にこういう本で知るのは難しいと思うんだ・・・。

API

ユーザーに公開するAPIについても、結構多くの所で説明されています。APIを内部の関数を外向けに公開します程度に考えてしまってぐだぐだになることがありますが、APIはそのアプリケーションの顔なので専門家がレビューすべきだと思う。

僕はAPIを「サービスを提供する起点だけでなくプロダクトをメンテする起点」と考えています。APIを変えずにどう中身をよくしていくか、またAPIを返るとしたらどういう戦略で変えていくのかそういう観点からの説明が多くあり、考え方は間違って無くてもっと良いAPIはどうあるべきを考えないといけないなと思いました。

アプリの設定(付録A)

アプリの設定をどうするかのトレードオフについてのまとめ。付録だけど良くまとまってました。

  • ソースコードに入れてコンパイル
  • コマンドライン引数
  • 環境変数
  • 設定ファイル

等など。

Python+JSON データ活用の奥義

Python+JSON データ活用の奥義が面白かったので紹介します。

Python+JSON データ活用の奥義 | クジラ飛行机 |本 | 通販 | Amazon

JSONを使ってあんなこと、こんなことをやってみようという本です。 とにかく扱っているトピックが広い。JSONを読み込んでグラフにして見る所から入り、ラズパイからの情報収集や、Webのスクレイピング、SNSの分析、QRコードを使ったクーポンサービス等、これでもかというくらいの事例が載っています。そのために必要なライブラリについても分かりやすくかいてあります。ラズパイ買うときの注意点や、スクレイピング時の注意点(岡崎市立中央図書館事件)等もしっかり書いてあります。素晴らしい。

表紙の雰囲気から、JSONの規格の細かいところとか、知られていないJSONテクニックとか、複数のプロセスで上手く読み書きする方法とかそういうのを期待して買ったのですが、期待と違う方向に突き抜けていて良かったです。ラズパイのLEDをJSONで制御するという文字を見たときに、狂気を感じました。とにかくJSONです。何をやるにもJSONです。

Pythonでどんなことが出来るかを知りたい人にお勧めです。ちょっと最近趣味プログラミングが進まないなという人にも、やってみよう的なアイデアが詰まっていました。

Pythonでセグメンテーションの領域を一回り大きく切り抜く

社内勉強会のネタにしようと思ったけど、せっかくなのでブログに書く。 既についているアノーテーションを一回り大きく取って、別の画像に張りつけるやりかたをまとめました。

Instance Segmatationのアノテーションを、一回り大きくくりぬきたいって時よくありますよね。 領域をくりぬいて資料に貼り付けるとか別の使い方をしたいときにギリで囲ってあると領域が見えなかったり、アノテーション雑すぎて対象物の境界情報がなくなってるとか。 ここで一回り大きくの定義を厳密に決めようすると難しいので、なんとなく一回り大きく取れていればOKくらいの感覚で先に進む。

入力データ

雑なアノーテーションで困ってます。

真面目な戦略

囲っている領域の点を全体的に外側に移動させれば良い。

ここで一つ問題がでてくる。

左側のような図形であれば真ん中から外側がなんとでも計算できるけど、右側のように凹凸がでてくると外側はどっちかをアルゴリズムで決める必要がある。

教科書的にやろうとすると多分こうなる。

  • 注目している点の2つの隣の点から線分を2つ作る。
  • 線分のなす角から真ん中の角度を求める。ここから点を動かす方向が2点に絞られる。
  • 両方の方向について、Segmentationで囲んだ領域の内側か外側かを判定する。これは専用のアルゴリズムがある。
  • 領域の外側に向かっている方向に、点を一定量移動させる。

だいぶ面倒。

画像処理のライブラリ使おう

手っ取り早く領域を広くする方法として、太い線で領域を描く方法と、領域に対してぼかす系のフィルターをかける方法がある。 太い線で十分だけど、そのあと別の画像に貼り付ける時はガウシアンフィルターが有効だったので両方紹介する。

左上が元々の領域。この領域を太い線で描画して、元の領域と重ね合わせると左中の領域になる。 線の太さ分だけ領域が大きくなっている。角が気になる場合は、角に丸を描画すると良い。

さらにぼかす系のフィルターとしてガウシアンフィルターをかけたものが左下の画像。ガウシアンフィルターの半径分だけ領域が増えている。これを合成用のアルファとして使う事で、合成を少しましにしている。ただ領域を増やしたいだけなら2値化すればよい。

どちらにしても広くする量をピクセルの単位で指定できるのがメリット。

何もしない場合

画像を読み込んで、背景画像を用意した後、マスク用の画像を用意する。 draw.polygon()で切り抜く領域をマスク用の画像に描画し、そのマスク用画像を使って合成する。

    bg = Image.new('RGB', (width, height), (0, 0, 255))

    # mask 画像を用意
    mask = Image.new("L", (width, height), 0)
    draw = ImageDraw.Draw(mask)
    draw.polygon(segment, fill=255)

    # maskを使って、背景に合成
    composite = Image.composite(im, bg, mask)

結果。アノーテーションの領域がそのまま貼り付けられる。

太い線で描く方法

draw.polygon()に引数widthがあるが、期待通りの動きをしないのでdraw.line()に太さを指定してマスク用画像を描画する。 draw.line()は塗りつぶしをしないので、線を引いた後、draw.polygon()で元々の領域を塗りつぶす。

    # 画像を太い線で広くする
    line_width = 30
    for i in range(len(segment)-1):
        draw.line((segment[i], segment[i+1]), fill=255, width=line_width)
    else:
        draw.line((segment[0], segment[-1]), fill=255, width=line_width)
    # 本来の場所の塗りつぶし
    draw.polygon(segment, fill=255)

結果。ちょっと領域が広くなっている。

ガウシアンフィルター

太い線を描いた後ガウシアンフィルターをかければOK。

    # 画像を太い線で広くする
    line_width = 30
    for i in range(len(segment)-1):
        draw.line((segment[i], segment[i+1]), fill=255, width=line_width)
    else:
        draw.line((segment[0], segment[-1]), fill=255, width=line_width)

    # ガウシアンフィルターをかける。
    mask = mask.filter(ImageFilter.GaussianBlur(radius=10))

結果。合成の境目にぼかしが入った。ぼかしは領域を広くする方向にのみ入る。(領域の内側はぼかしが聞いていない)。

効果が分かりやすいように単色背景にしているが、自然画像を背景にするとちょっと良さが分かると思う。やってみて。

ソースコード

import os
import PIL
from PIL import Image, ImageDraw, ImageFilter

input_file = 'img/sta.jpg'

# segmentationの座標リスト
segment = [(8, 1665), (98, 1515), (248, 1334), (430, 1187), (705, 1112), (1202, 1250), (1345, 1390), (1467, 1559), (1505, 1687), (736, 1965), (14, 2000)]


# 1. そのまま背景に合成する
def proc1():
    im = PIL.Image.open(input_file)

    # 画像のサイズを取得
    width, height = im.size
    # 青の背景を用意
    bg = Image.new('RGB', (width, height), (0, 0, 255))

    # mask 画像を用意
    mask = Image.new("L", (width, height), 0)
    draw = ImageDraw.Draw(mask)
    draw.polygon(segment, fill=255)

    # maskを使って、背景に合成
    composite = Image.composite(im, bg, mask)

    # 保存
    composite.save('img/proc1.jpg')

def proc2():
    im = PIL.Image.open(input_file)

    # 画像のサイズを取得
    width, height = im.size
    # 青の背景を用意
    bg = Image.new('RGB', (width, height), (0, 0, 255))

    # mask 画像を用意
    mask = Image.new("L", (width, height), 0)
    draw = ImageDraw.Draw(mask)

    # 画像を太い線で広くする
    line_width = 30
    for i in range(len(segment)-1):
        draw.line((segment[i], segment[i+1]), fill=255, width=line_width)
    else:
        draw.line((segment[0], segment[-1]), fill=255, width=line_width)
    # 本来の場所の塗りつぶし
    draw.polygon(segment, fill=255)

    # maskを使って、背景に合成
    composite = Image.composite(im, bg, mask)

    # 保存
    composite.save('img/proc2.jpg')

def proc3():
    im = PIL.Image.open(input_file)

    # 画像のサイズを取得
    width, height = im.size
    # 青の背景を用意
    bg = Image.new('RGB', (width, height), (0, 0, 255))

    # mask 画像を用意
    mask = Image.new("L", (width, height), 0)
    draw = ImageDraw.Draw(mask)

    # 画像を太い線で広くする
    line_width = 30
    for i in range(len(segment)-1):
        draw.line((segment[i], segment[i+1]), fill=255, width=line_width)
    else:
        draw.line((segment[0], segment[-1]), fill=255, width=line_width)

    # ガウシアンフィルターをかける。
    mask = mask.filter(ImageFilter.GaussianBlur(radius=10))
    draw = ImageDraw.Draw(mask)

    # 本来の場所の塗りつぶし
    draw.polygon(segment, fill=255)

    # maskを使って、背景に合成
    composite = Image.composite(im, bg, mask)

    # 保存
    composite.save('img/proc3.jpg')


if __name__ == '__main__':
    # 3つのアルゴリズムを比較する
    # 1. そのまま背景に合成する
    # 2. 画像を太い線で広くしてから合成する
    # 3. 2.に加え、ガウシアンフィルターをかけて合成する。

    # 1. そのまま背景に合成する
    proc1()
    # 2. 画像を太い線で広くしてから合成する
    proc2()

    # 3. 2.に加え、ガウシアンフィルターをかけて合成する。
    proc3()

初めての逆運動学

実践ロボット制御を読みながら、三章の脚ロボットの逆運動学をプログラミングしてみた。

実践 ロボット制御: 基礎から動力学まで | アールティ, 耕, 細田 |本 | 通販 | Amazon

数式は本を見てもらうとして、素直にコーディングするとこんな感じ。

まあまあそれっぽくは動いている。

ただ、このプログラムは、この範囲でしか上手く動かない。 少し範囲を変えるとこうなってしまう。

多分緑の角度を決めているθの符号が逆。

ここの角度にarccosを使っていて、cos(θ)=cos(-θ)だからかなと思ってる。

課題は2つ

  • θの符号はどう決めるか。
  • 単純にIKを解くだけだと、ロボがワープするから今の姿勢から最終姿勢までの連続として解かないといけない

4章以降で運動学の一般化の説明があるから、勉強続ければ分かると思う。

以下、コード。 アニメgif化するところはcopilotがだいたい書いてくれた。

import PIL
from PIL import Image, ImageDraw
import math
import numpy as np
import os
import glob

import matplotlib.pyplot as plt
import matplotlib.animation as animation

# キャンパスの大きさ
CANVAS_WIDTH = 300
CANVAS_HEIGHT = 300

GROUND = -80

PI = math.pi


class Robo1leg:
    def __init__(self):
        self.theta1 = PI / 8
        self.theta2 = - PI / 8
        self.theta3 = PI / 2
        self.l1 = 50
        self.l2 = 40
        self.l3 = 20

    def draw(self, canvas):
        draw = ImageDraw.Draw(canvas)
        # 本体
        cx0, cy0, cz0 = robo2canvas(0, 0, 0)
        draw.ellipse((cx0 - 10, cy0 - 10, cx0 + 10, cy0 + 10), fill=(0, 0, 0))

        # 足1
        x1 = self.l1 * math.sin(self.theta1)
        z1 = - self.l1 * math.cos(self.theta1)
        cx1, cy1, cz1 = robo2canvas(x1, 0, z1)
        draw.line((cx0, cy0, cx1, cy1), fill=(255, 0, 0), width=3)

        # 関節
        draw.ellipse((cx1 - 5, cy1 - 5, cx1 + 5, cy1 + 5), fill=(0, 0, 0))

        # 足2
        x2 = x1 + self.l2 * math.sin(self.theta2 + self.theta1)
        z2 = z1 - self.l2 * math.cos(self.theta2 + self.theta1)
        cx2, cy2, cz2 = robo2canvas(x2, 0, z2)
        draw.line((cx1, cy1, cx2, cy2), fill=(0, 255, 0), width=3)

        # 関節
        draw.ellipse((cx2 - 5, cy2 - 5, cx2 + 5, cy2 + 5), fill=(0, 0, 0))

        # 足3
        x3 = x2 + self.l3 * math.sin(self.theta3 + self.theta2 + self.theta1)
        z3 = z2 - self.l3 * math.cos(self.theta3 + self.theta2 + self.theta1)
        cx3, cy3, cz3 = robo2canvas(x3, 0, z3)
        draw.line((cx2, cy2, cx3, cy3), fill=(0, 0, 255), width=3)

    def get_leg_pos(self):
        x1 = self.l1 * math.sin(self.theta1)
        z1 = - self.l1 * math.cos(self.theta1)
        x2 = x1 + self.l2 * math.sin(self.theta2 + self.theta1)
        z2 = z1 - self.l2 * math.cos(self.theta2 + self.theta1)
        x3 = x2 + self.l3 * math.sin(self.theta3 + self.theta2 + self.theta1)
        z3 = z2 - self.l3 * math.cos(self.theta3 + self.theta2 + self.theta1)

        return x3, z3

    def inverse_kinematics(self, x, y, z):
        theta1 = math.atan2(
            (self.l1 + self.l2 * math.cos(self.theta2)) * x + self.l2 * math.sin(self.theta2) * z,
            (self.l2 * math.sin(self.theta2) * x - (self.l1 + self.l2 * math.cos(self.theta2)) * z)
        )
        tmp = (x**2 + z**2 - self.l1**2 - self.l2**2) / (2 * self.l1 * self.l2)
        theta2 = math.acos(tmp)
        theta3 = (PI / 2) - theta1 - theta2

        return theta1, theta2, theta3

class World:
    def __init__(self):
        # 画像内でxは右が正、zは上が正、yは手前が正
        self.origin_x = int(CANVAS_WIDTH / 2)
        self.origin_z = int(CANVAS_HEIGHT / 2)
        self.output_dir = 'result'

        self.robo_x = 0
        self.robo_y = 0
        self.robo_z = 0
        self.robo = None

    def add_robo(self, robo):
        self.robo = robo

    def draw(self, time):
        canvas = Image.new('RGB', (CANVAS_WIDTH, CANVAS_HEIGHT), (255, 255, 255))
        self.robo.draw(canvas)

        gx, gy, gz = robo2canvas(0, 0, GROUND)
        draw = ImageDraw.Draw(canvas)
        draw.line((0, gy, CANVAS_WIDTH, gy), fill=(0, 0, 0), width=1)

        file_name = os.path.join(self.output_dir, 'result_{0:03d}.png'.format(time))
        canvas.save(file_name, 'PNG')



def robo2canvas(robo_x, robo_y, robo_z):
    """ロボの座標を画像上のx, yに変換する。"""
    rot_mat = np.array([[1, 0, 0, CANVAS_WIDTH / 2],
                        [0, 0, -1, CANVAS_HEIGHT / 2],
                        [0, 0, 0, 0]])

    pos = rot_mat.dot(np.array([robo_x, robo_y, robo_z, 1]))
    return pos[0], pos[1], pos[2]

def create_animation(output):
    fig = plt.figure()

    pictures = glob.glob("result/*.png")

    ims = []
    for i in range(len(pictures)):
        # 読み込んで付け加えていく
        tmp = Image.open(pictures[i])
        ims.append([plt.imshow(tmp)])

    ani = animation.ArtistAnimation(fig, ims, interval=500)
    ani.save(os.path.join('result', output))


if __name__ == '__main__':
    robo = Robo1leg()
    world = World()
    world.add_robo(robo)

    px0, pz0 = 40, GROUND
    theta1, theta2, theta3 = robo.inverse_kinematics(px0, 0, pz0)
    robo.theta1 = theta1
    robo.theta2 = theta2
    robo.theta3 = theta3

    print("robo pos = ", px0, pz0)
    pxf = - px0
    pzf = pz0
    step = 16

    for t in range(0, step):

        # zを一定にして逆運動学で目標位置を求める
        px = px0 + (pxf - px0) * t / step
        pz = pz0 + (pzf - pz0) * t / step
        print("target pos = ", px, pz)

        theta1, theta2, theta3 = robo.inverse_kinematics(px, 0, pz)
        robo.theta1 = theta1
        robo.theta2 = theta2
        robo.theta3 = theta3
        world.draw(t)

    create_animation('result.gif')

初心者が言語モデルを勉強するための本(2023年6月版)

流行のLLMを勉強したくて沢山本を読みました。 この後もしばらくLLM(GPT)関係の出版が続きそうなので、現状の本でまとめてみました。

参考:

nowokay.hatenablog.com

まとめ。

  • Transformerの仕組みを知りたい人で、画像のDeep Learningなら分かるって人はVision Transformer入門
  • 言語モデルをデータセットを作る所からやってみたい人には、作ってわかる! 自然言語処理AI
  • とにかくすぐに動かしたい人には、機械学習エンジニアのためのTransformers
  • ビジネス的に何ができるのかを知りたい人はBERT入門

Vision Transformer入門

Vison Transformerになっていますが、Transformerの説明がとても詳しくお勧めです。実際に写経してパーツパーツで動かせるのはこの本だけ。Transformer一点突破なので、一般的な言語処理の説明は弱いのですが、それを差し引いてもTransformerの説明が一番分かりやすかったです。特にDeep Learningが画像から入った人には、CNNとの比較もありすごく分かりやすかったです。

後半は事例になっていて、実際にTransformerを記述するところはボリューム無いので動かして見るにはちょうど良いと思います。

言語モデルを動かさないと意味が無いって人には、次の作ってわかる! 自然言語処理AIがお勧め。

gihyo.jp

作ってわかる! 自然言語処理AI

写経してTransformerを理解した人にお勧め。word2vecとそれ以降の言語モデルの違い等、理論的な所もしっかり押さえてあります。自分でwikipediaのデータをダウンロードして学習して行きます。Transformerのアーキテクチャだけでなく、学習データの作り方までしっかりわかるのはこの本だけ。いろいろあるAttentionの違いがちゃんと説明してあるのもこの本だけでした。

自分で言語モデルを1から学習したい人にお勧めです。

www.c-r.com

機械学習エンジニアのためのTransformers

何か動かしたくて、一冊だけ買うならこの本です。Transformersはライブラリの名前です。複数形なので区別がつくはず。

Transformers(ライブラリ)の使い方、Transfsormersで解けるタスクの紹介と実際の動かし方、ONNXやプルーニングを使った高速化手法まで、盛りだくさんの内容になっています。

Transformer自体の説明もちゃんとあります。個々の説明が駆け足にも関わらず、要所要所で技術的な鋭い補足が入っています。

具体的にやりたいことがあって最短距離進みたいならこの本しか無いですね。

www.oreilly.co.jp

ディープラーニングによる自然言語処理

Transformerメインではなく、自然言語処理全般について書かれている本。 AllenNLPというライブラリを使っていて、取り扱っているトピックの割には本が薄めになっていて読みやすいです。最後はBERTまで。薄いながらも評価基準がしっかり書いてあって、しれっとoptunaも使っています。

LSTMの説明もしっかりしてあり、Transformerを動かすのではなく、Deep Learningを使った言語処理の研究をしたい人向けでした。

www.kyoritsu-pub.co.jp

BERT入門

言語モデルが解くべきタスクの説明や、データアセスメントについてよくまとまっていました。本格的に動かす前に、言語モデルが何できるのかを知りたいビジネス寄りの人にお勧め。実装に関しては、Vision Transformer入門 (Computer Vision Library) が分かりやすいと思う。

www.nttdata.com

深層学習からマルチモーダル情報処理

not for meだった本。 深層学習について、誤差関数とか基本的な所から詳しく説明されています。ただ、JDLAのE資格もっていれば全部知っている内容でした。

また、期待していたマルチモーダル部分についてもあまり記載がなく残念でした。もう少し、マルチモーダル故の課題とか、解決策が載っていて欲しかった。

www.saiensu.co.jp