catalinaの備忘録

ソフトウェアやハードウェアの備忘録。後で逆引きできるように。

RustとOpenCVを組み合わせた画像処理

rustが楽しくなってきました。かたりぃなです。 今回はrustでopencvを使って画像分類に挑戦しようと思います。

その前に。記事のタイトルをAIが生成してくれる機能がついたみたいなので試してみました。 だいたいあってる気がします。

それでは本編。

まず環境準備。 rustはインストール済みなので、アップグレードだけします。 opencvはソースからビルドするのめんどいのでバイナリ持ってきます。

rustのアップグレード

PS C:\myproject\tauriapp> rustc --version
rustc 1.70.0 (90c541806 2023-05-31)
PS C:\myproject\tauriapp> cargo --version
cargo 1.70.0 (ec8a8a0ca 2023-04-25)
PS C:\myproject\tauriapp> rustup --version
rustup 1.26.0 (5af9b9484 2023-04-05)

PS C:\myproject\tauriapp> rustup self update
info: checking for self-update
  rustup unchanged - 1.26.0
PS C:\myproject\tauriapp> rustup update stable

PS C:\myproject\tauriapp> rustc --version
rustc 1.76.0 (07dca489a 2024-02-04)
PS C:\myproject\tauriapp> cargo --version     
cargo 1.76.0 (c84b36747 2024-01-18)

opencvのインストール

参考資料 https://qiita.com/benki/items/f810495f9d430db80129

https://github.com/opencv/opencv/releases/tag/4.3.0 から https://github.com/opencv/opencv/releases/download/4.3.0/opencv-4.3.0-vc14_vc15.exe をダウンロードして展開。

D:\oss\ に展開指示すると、D:\oss\opencv というディレクトリが作られ、ここに展開されます。

以下の環境変数を設定 PATH=$PATH;D:\oss\opencv\build\x64\vc15\bin OPENCV_LINK_LIBS=opencv_world430 OPENCV_LINK_PATHS=D:\oss\opencv\build\x64\vc15\lib OPENCV_INCLUDE_PATHS=D:\oss\opencv\build\include

これでopencvのインストールまで完了です。

画像分類の手法の検討

手法はいろいろあって、すぐ思いつく範囲でも

などがあります。

今回は特徴表現による画像分類として、固有空間での画像分類を試してみようと思います。

固有空間での画像分類の概要

このアルゴリズムを使うのは初めてなのですが、自分が理解した範囲では 「PCAして次元圧縮した表現上で画像比較すれば早いよね」 くらいに解釈しました。

乱暴にいうと 「ビットマップのペイロード同士を比較ではなく、jpegのDCT係数だけ比較したほうが早いね」 くらいの感じかなと。根本的には基底ベクトルが異なるので、あくまでイメージです。

そんなわけでアルゴリズムとしては次のようになります。 便宜上、最近の機械学習のモデルのように学習フェーズと推論フェーズでわけて考えます。 (実際には学習ではなくただの事前準備ではありますが。。。)

学習フェーズ -> 平均画像, 固有値, 固有ベクトル, ラベル画像の係数が得られる。

  • PCA
    • ラベル画像の集合を1つのmatとして
    • 画像セット全体に対してPCA -> 平均画像、固有値固有ベクトル が得られる
  • 各画像の係数を求める
    • 各画像を固有空間へ射影する -> 固有値

推論フェーズ

  • 入力画像を固有空間へ射影する -> 固有値
  • 固有空間上で
    • 入力画像と各ラベル画像を比較し、固有値同士の差分を求める -> 固有空間上の画像diff
    • 固有空間上の画像diffに対し、固有ベクトルの寄与度を加味する -> 類似度を表す配列が得られる
  • 一番類似している画像ラベルを返す

こんな感じです。

それでは実際のコードで見てみます。

学習フェーズの全体像

ちょっと実験していてややこしくなっていますが、ラムダを返したかったのです。

ディレクトリの指定は実行時ではなく事前に行いたくて、引数をキャプチャしたラムダを返してます。 関数型言語でいうカリー化ってやつですね。

このラムダは画像が格納されたディレクトリからすべての画像を読み込み、固有値分解を行い、各画像を射影して係数を求めます。 Eigenの定義はこんな感じです。

pub struct Eigen{
    pub mean: Mat,
    pub eigen_vec: Mat,
    pub eigen_val: Mat,
}
// workdirから画像を読み込んで、固有値と係数を求める
fn fn_find_eigenvalue(cardimage_dir:&str) -> impl FnOnce() -> Result<(Eigen, Mat), Box<dyn std::error::Error>> + '_
{
    move ||{
        // 全ての画像を読み込む
        let images = read_images(&cardimage_dir)?;

        // 画像の事前処理
        let cropped_images = images.iter().map(
            preprocess_image
        ).collect::<Vec<Mat>>();

        // 固有値を求める
        let eigen = pca_images(&cropped_images)?;

        // 各画像の係数を求める
        let img_coeff = project_images(&cropped_images, &eigen)?;

        Ok((eigen, img_coeff))
    }
}

事前処理はこんな感じです。グレースケールにしてる理由は実験を早く回したいためです。

pub fn preprocess_image(img: &Mat) -> Mat{
    let cropped_image = Mat::roi(img, card_ilust_rect()).unwrap();  // イラスト部分のみ抽出
    let mut gray = Mat::default();
    cvt_color(&cropped_image, &mut gray, COLOR_RGB2GRAY, 0).unwrap();   // グレースケール化する
    let gray1d = gray.reshape(1,1).unwrap();                        // 一次元化
    let mut gray1df = Mat::default();
    let _ = gray1d.assign_to(&mut gray1df,  CV_32FC1);                             // float化
    gray1df
}

pca処理はこんな感じです。 26600ピクセル(190x140)の画像N個がVec[N]の型で与えられたとして、 Mat[N, 26600]にしてからPCAにかけています。 PCAの出力ベクトルは一旦256個としました。

pub fn pca_images(images: &Vec<Mat>) -> Result<Eigen, Box<dyn std::error::Error>>{
    // 形状を変更( vec<mat> => mat )
    let mut mat_images = Mat::default();
    for img in images{
        mat_images.push_back(img)?;
    }
    let mut mean = Mat::default();
    let mut eigen_vec = Mat::default();
    let mut eigen_val = Mat::default();
    pca_compute2(&mat_images, &mut mean, &mut eigen_vec, &mut eigen_val, 256)?;

    // 190x140
    // 平均画像の確認用
    // let mean2d = mean.reshape(1,400)?.clone();
    // let img = imwrite("mean.png", &mean2d, &Vector::new() )?;
    Ok(Eigen::new(mean, eigen_vec, eigen_val))
}

各画像の係数を求める関数です。 pca_projectをすべてのラベル画像に行っているだけですね。 ちなみにpca_compute2, pca_projectはopencvの関数です。

pub fn project_images(images: &Vec<Mat>, eigen: &Eigen) -> Result<Mat, Box<dyn std::error::Error>> {
    let mut projected_images = Mat::default();
    for img in images{
        let mut projected_image = Mat::default();
        pca_project(&img, &eigen.mean, &eigen.eigen_vec, &mut projected_image)?;
        projected_images.push_back(&projected_image)?;
    }
    Ok(projected_images)
}

この結果、 mean(平均画像), eigen_vec(固有ベクトル), eigen_value(固有値), img_coeff(固有空間上での各画像の係数) が得られました。

推論フェーズ

推論処理はこのようになりました。

fn classify_image(eigen:Eigen, coeff:Mat, img:&Mat) -> Result<usize, Box<dyn std::error::Error>> {
    // 前処理
    let preprocessed_img = preprocess_image(&img);

    // 固有空間へ射影する
    let mut projected_image = Mat::default();
    pca_project(&preprocessed_img, &eigen.mean, &eigen.eigen_vec, &mut projected_image)?;

    // 固有空間上での入力画像表現と分類先画像のcoefを比較
    let eigen1d = eigen.eigen_val.reshape(1,1)?.clone();
    let mut diff_images = Vec::new();
    for i in 0..img_coeff.rows(){
        if let Ok(row) = img_coeff.row(i){
            let diff = mat_absdiff(&row, &projected_image); // todo: 二乗和誤差のほうがいいのでは?
            let diff_residual = residual(&diff, &eigen1d);  // 固有値の寄与度を加味する
            diff_images.push(diff_residual);
        }
    }
    println!("diff_image_len = {:?}", diff_images.len() );

    // coefとの誤差が最小であるインデックス = 目的の画像である
    // todo : 足きりしたほうがいい?(=信頼度が低い予測は捨てる)
    let mut min_val = std::f32::MAX;
    let mut min_index= 0;
    for i in 0..diff_images.len(){
        if diff_images[i] < min_val {
            min_val = diff_images[i];
            min_index = i;
        }
    }
    Ok(min_index)
}

前半部分の特徴空間への射影までは推論フェーズと同じです。 後半の固有空間上での比較は絶対値の差分での比較となっています。よく使われる二乗和誤差のほうがいいのかもしれませんが自信ないです。

というわけで、このコードでとりあえずの画像分類ができました。 全画像を比較するよりは高速に動作するので、一つの方法としてアリでは?と思っています。

それでは今回はこれくらいで。

tauriでテトリスっぽいものを作ってみる

rustの勉強をしています。かたりぃなです。 勉強といっても写経だけじゃあつまらないので、実際に何かを作ってみようと思います。

個人的にはエンターテインメント的なもの、たとえば簡単なゲームとかがモチベーションが上がって良いです。

あまり複雑なものを作ると設計上の問題も懸念されるので、簡単にできるテトリスを作ってみようと思います。

初めてtauriでアプリを作るにあたって、詰まったポイントなどをメモしておきます。

ちなみに完成したのはこんな感じのやつです。

jsからrustを呼び出す

jsから呼び出し可能な関数をrustで書く手順は次のとおりです。

  1. 関数定義に#[tauri::command]を付与する
  2. tauriのinvoke_handlerに登録する
  3. jsからは invoke("関数名", "json形式のパラメータ") で呼び出す
  4. struct等を受け渡すときはserdeクレートを使う

たとえばキー入力を受け取る関数はこんな感じに定義できます。 #[tauri::command]がついただけですね。

#[tauri::command]
fn keyinput(
    key: char
) -> GameField{
    ~~関数本体~~
}

この関数をアプリケーション起動時にinvoke_handlerとしてtauriに登録します。

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![
            keyinput
        ])

これをjsから呼び出すコードは以下のようになります。

  async function keyDownHandler(e) {
    if(e.key === 'a' || e.key === 'w' || e.key === 's' || e.key === 'd'){
      var gf = await invoke("keyinput", {key:e.key});

この方式でのデータの受け渡しはjson形式になるようです。 structを受け渡すのであれば、serdeクレートを使ってシリアライズできるよう定義します。enumとかも同様に、serdeクレーとのドキュメント通りにやればOKです。

#[derive(Serialize, Deserialize)]
pub struct GameField{
    field: [[i32; 10]; 20],
}

tauri(rust)で状態管理

rustではmutableなグローバル変数は使えません。(頑張れば使えるらしいですが、そういうことはしない方が良いです。) すなわちゲームの状態を管理できないということで、いきなり困ってしまいました。

これ系の話では、往々にしてフレームワーク側が管理する仕組みを用意していたりします。 確かC++のboostの中にもそういうのが幾つもあった気がします。

実際、tauriにもstateを管理するという機能が用意されています。 (https://tauri.app/v1/guides/features/command/#accessing-managed-state)

というわけで、今回作った状態管理はこんな感じです。

  1. 状態管理用の構造体を定義し、Mutexでラップする(書き換えらえるように)
  2. tauriのsetup時にapp.manage("状態管理構造体")を渡す
  3. #[tauri::command]の関数でtauri::State<'_, 状態管理構造体>として受け取る

まず状態を管理する構造体を適当に作ります。 GameFieldは固定された盤面の状態を管理するもの、 FallingBlockが落ちてきているブロック、すなわち動かせるブロックの情報です。

pub struct GameManager{
    field: Mutex<GameField>,
    block: Mutex<FallingBlock>,
}

このstructを、tauriのアプリケーション起動時パラメータに設定します。 setupの中でGameManagerを生成し、それをmanageで登録するというものです。これでtauriが「状態として管理」してくれます。

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![
            get_gamefield,
            update_gamefield,
            keyinput
        ])
        .setup(|app|{
            let gamemanager = GameManager::new();
            app.manage(gamemanager);    //State変数として登録する
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");

こうして登録したstateは、次のようにして使うことができます。 #[tauri::command]を指定した関数に渡してくれるようになります。

あとはGameManagerに適当な関数を定義しておけば、状態の読み書きができるようになります。

#[tauri::command]
fn keyinput(
    state: tauri::State<'_, GameManager>,
    key: char
) -> GameField{
    let mut x_pos = state.get_xpos();
    ~~ キー入力に応じた処理 ~~
    state.set_xpos(x_pos);

ちなみに、get,setの実装ではmutexをロックして操作する必要があります。

impl GameManager{
    pub fn get_xpos(&self) -> i32{
        let param = self.update_param.lock().unwrap();
        param.x_pos
    }
    pub fn set_xpos(&self, x_pos:i32){
        let mut param = self.update_param.lock().unwrap();
        param.x_pos = x_pos;
    }

reactコンポーネントを複数作るときはkeyを指定する必要がある

reactでブロックや空白を敷き詰めてゲームフィールドを再現しようとしたとき、 こんな感じのコードになりました。

<div className="gamefield">
    gamefield.flat().map((s, index) => s.fill === 'Block' ?
        <div key={index} className="block"></div> :
        <div key={index} className="Space"></div>)
</div>

gamefieldのfill要素には'Block'か'Space'が入っていて、それを出し分けつつ複数個生成してるコードです。 key={index}はreactが必要としているもので、付与しておかないとコンソールにエラーが出てきてしまいます。

ちなみにflattenしてしまっているのは、ゲームフィールドの縦横幅を固定とし、gridもそのように調整したので、順に生成していくだけでいい感じに折り返してくれるようになりました。

.block{
  width: 30px;
  height: 30px;
  background-color: green;
}

.space{
  width:30px;
  height:30px;
  background-color: aliceblue;
}

.gamefield{
  display: grid;
  grid-template-columns: repeat(10, 30px);
  grid-template-rows: repeat(20, 30px);
  column-gap: 2px;
  row-gap: 2px;
  width: 320px;
  height: 640px;
  background-color: gray;
}

rustで複数のラムダを統一的に扱いたい

ゲームロジックの実装で

  1. 入力を取得する
  2. 入力をもとに盤面を更新
  3. 更新結果を反映

みたいなのがあります。

このとき、3のロジックを1の段階で切り替えたいことがあります。 移動方向によってstateをどう更新するかが変わるから等です。 解決策として1の段階でラムダを生成し、3の段階ではそのラムダを使うようにすればいいのではと考えました。 次のようになりました。

    let postproc:Box<dyn Fn(&tauri::State<'_, GameManager>)->()>  = match key{
        'a' => {
            x_pos = x_pos - 1;
            Box::new(move |state|{state.set_xpos(x_pos);})
        },
        'd' => {
            x_pos = x_pos + 1;
            Box::new(move |state|{state.set_xpos(x_pos);})
        },
        ~~ 略 ~~
    };

    ~~ゲームのロジック~~
    if ~~~ {
        postproc(&state);
    }

ポイントは let postproc:Box<dyn Fn(&tauri::State<'_, GameManager>)->()>の部分で、 これによりmatchアーム部のラムダを統一的に扱える型で受け取ることを明示します。

まだ完全には理解できていませんが、大体そんな感じみたいです。

イテレータとかファンクタを使いこなしたい

Cとかで書くと、forでループカウンタまわして。。。みたいなのが多発しがちですが、rustには関数型言語らしい機能が多々あるので大いに利用していきたいです。

たとえばフィールド中の一行が全てブロックで埋まっている行を抽出する処理はこうなりました。

for row in field.iter() {
    if row.iter().all(|x| *x == 'Block'){
    }
}

field.iter()で、上から下へ走査していき、 row.iter().all(|x| *x == 'Block')で、横一列が特定の値か(=ブロックで埋まっている)を抽出します。 この記事を書いてて気づきましたが、外側のforもmapで書けばすっきりしそうですね。

ちなみにこのコードはBingAIに質問しつつ作ったものです。 この例のようにAIの回答通りでは欲しいものにはならないので、追加の質問をしたり、提示されたコードを参考に別の方法を探すなどの対応は必要そうですね。。

tauriを試してみる

お久しぶりです。tauriというものを試してみようと思います。かたりぃなです。

tauriとはweb技術を使ってデスクトップアプリを開発するフレームワーク的なやつらしいです。 https://tauri.app/

似たような話でelectronがありますが、electronはブラウザ(Chromium)を内包しているのに対し、tauriはwebviewを使うという点が違うらしいです。

どうしてtauri?

一年ほど前からUWPをひっそりと縮小していくという話がありました。SDKが変わるのはよくある話なのですが、果たしてこのまま付いていっていいのかなという疑問が自分の中で出てきました。

私はHololensのアプリを作るためにUnityとUWP(C++/CX, +DirectX)から入りましたが、C++/CXは終了しWinRTになりました。 DirectXもバージョンアップを繰り返していて、大昔にDirectX5を触ったころは固定機能パイプラインだけだったのですが、DirectX11ではシェーダーを自力で書く必要があって、楽しいながらも骨の折れる作業だった記憶があります。

そしてUWP縮小の話を見ていると、このまま趣味でMS技術を追っていくことは時間リソース的に不可能なのではと思い始めました。もっと効率のいい開発方法が欲しいわけで、最近のデスクトップアプリ開発ってどんなだろうと見てるとtauriがあがってきたわけです。

RustとJSという組み合わせも、ちょうど興味があったところです。

Rustについて色々調べてみたところ、Linuxカーネル側にも利用していくくらいに信頼ができてきています。 また、tauriの技術要素であるJavascriptに関して言えば、3Dレンダリングくらいは普通にできて、ライブラリも充実しています。 となれば、まあ趣味でやっていくぶんにはこれでいいんじゃないかなと思った次第です。

開発環境を作る

Windowsの開発環境づくりというのはいつもはVisualStudioに任せっきりだったので、実際どういう構成にするのが正解なのかよくわかりません。 たぶんマニュアル通りにやれば行けそうな気がしますが、自信ないです。

開発環境づくりで躓いて諦めるという王道パターンは踏みたくないので、慣れてる環境を拡張する方向でいきます。

幸いXWindowシステムといってGUIを転送する手段があります。 雑にいうとアプリケーション単位でのリモートデスクトップみたいなものです。

Linux上でtauriアプリを起動し、その画面をXWindowシステムでWindowsに転送してみることにします。

環境は以下の通りです。

  • ホストOS : WIndows10 pro
  • W-Windowサーバ : vcxsrv
  • 仮想環境 : Docker Desktop for windows
  • エディタ : VS-code

vcxsrv(https://sourceforge.net/projects/vcxsrv/)はあらかじめwindowsにインストール済みとします。

dockerでLinux環境を作る

作業ディレクトリを作ってVS-codeで開き、2つのファイルを作ります。 - dockker-compose.yml - docker/app.dockerfile

それぞれのファイルの中身は以下のようにします。

app.dockerfile

FROM ubuntu:latest

ENV DEBIAN_FRONTEND noninteractive

RUN sed -i.org -e 's|ports.ubuntu.com|jp.archive.ubuntu.com|g' /etc/apt/sources.list \
    && apt-get update && apt-get install -y \
        # xwindow
        x11-apps \
        # node.js
        curl nodejs npm \
        # TAURI
        libwebkit2gtk-4.0-dev build-essential wget libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Rust install
ENV RUST_HOME /usr/local/lib/rust
ENV RUSTUP_HOME ${RUST_HOME}/rustup
ENV CARGO_HOME ${RUST_HOME}/cargo
RUN mkdir /usr/local/lib/rust && \
    chmod 0755 $RUST_HOME
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > ${RUST_HOME}/rustup.sh \
    && chmod +x ${RUST_HOME}/rustup.sh \
    && ${RUST_HOME}/rustup.sh -y --default-toolchain nightly --no-modify-path
ENV PATH $PATH:$CARGO_HOME/bin

docker-compose.yml

version: '3'
services:
  rust:
    build: 
      context: ./docker
      dockerfile: app.dockerfile
    tty: true
    environment:
      - CARGO_BUILD_TARGET_DIR=/work/rust-targetdir
    volumes:
      - ./project-dir:/work/project-dir
      - node_volume:/work/project-dir/node_modules
      - rust_volume:/rust-targetdir

volumes:
  node_volume:
    driver: local
  rust_volume:
    driver: local

あとは docker-compose build すればOKです。

中に入って作業するために、VS-codeの「Ctrl+Shift+P」から remote-container: open folder in container で開いて作業していきます。

ここからはdockerコンテナ上での作業になります。

xwindowの動作確認

起動したらまずxwindowの動作確認をします

windows側でxwindowサーバ起動し、設定画面で以下2つを設定しておきます。

  • Access Control=Disable」にチェック
  • AccessControlすぐ下のオプションパラメータに「-nowgl」を入力

xwindowサーバ起動後、vscode上のターミナルから

export DISPLAY=<windowsPCのIP>:0.0
xeyes

を入力し、Windows上にxeyesのアプリが表示されることを確認します。

tauriの環境構築

アプリを作る前に、追加モジュールのインストールをします。

cd /work/project-dir
cargo install tauri-cli
npm install -g yarn

jsのフレームワーク(vueとかreactとか)がうまく入らないことがあるのでnodeを最新版にします。 このあたりのことはまだよくわかってないです。

npm install -g n
n lts

tauriアプリを作る

これでやっとアプリを作れます。

cd /work/project-dir
yarn create tauri-app

色々質問が出るので、お好みで。今回はfrontはreact-js, backはrustにしました。

# yarn create tauri-app
yarn create v1.22.19
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Installed "create-tauri-app@3.1.2" with binaries:
      - create-tauri-app
✔ Project name · tauri-app
✔ Choose which language to use for your frontend · TypeScript / JavaScript - (pnpm, yarn, npm)
✔ Choose your package manager · yarn
✔ Choose your UI template · React - (https://reactjs.org/)
✔ Choose your UI flavor · JavaScript

Please follow https://tauri.app/v1/guides/getting-started/prerequisites to install the needed prerequisites, if you haven't already.

Done, now run:
  cd tauri-app
  yarn
  yarn tauri dev

Done in 13.38s.

ビルド前にやること

このままビルドするとめっちゃ時間かかります。 node-modulesをwindows側に見せると重くなるためです。

windows側に見せない設定のディレクトリを/work/project-dir/node-modulesに用意しているので、/work/project-dir/に作成したtauriアプリのコード一式をもってきます。

cd /work/project-dir
mv tauri-app/* .
mv tauri-app/.* .
rmdir tauri-app

やっとアプリを起動です。

  cd /work/project-dir
  yarn
  yarn tauri dev

tauriアプリが起動しました。 何かしらの物が出てくるとモチベーションあがりますね。 少しづつ色々ためしていこうと思います。

コードをざっと眺めてみると、 - srcディレクトリの下がjsで作られたui - src-tauriの下がrustで書かれたバックエンド

という構成のようです。

今回のポイント

単にビルドするだけですが、工夫しておかないと重くて使い物にならないということがわかりました。 具体的には

  • node_modulesはコンテナ内で閉じておく
  • rustの出力先もコンテナ内で閉じておく

の2つが今回のポイントです。

なお、rustのビルド成果物の出力先を変更しているので、デバッガの設定するときなどは気をつけないといけないかもしれませんね。

感想と今後の展望

アプリの開発環境ができたので色々試してみようと思います。 - Rustのデバッグ設定をする - CライブラリをRustから利用する - Windows向けリリースパッケージを作る - JSで画面を作っていってみる などなど、試してみたいことはたくさんあります。

それでは今回はこれくらいで。

情報処理技術者試験について思うこと

こんな記事が話題になっていたので乗っかってみます。

他人の出した話題、特に今回のような匿名ダイアリーにはあまり乗っからないのですが、昔の自分を見ているような気がしたので。

情報処理技術者試験なんて何の役にも立ちません

ちなみに私は最後に情報処理技術者試験受けてから6年くらい経ってるので、今は実情が変わっているかもしれません。あしからず。

情報処理技術者試験とは?

ひとまず試験区分一覧を見てみましょう。

IPA 独立行政法人 情報処理推進機構:試験制度:試験区分一覧

有名どころでは下端にある基本情報とその上の応用情報ですね。で、 基本情報から上に向かうほど高度になります(だいたいのイメージです)。

で、上部の「高度な知識・技能」の灰色枠にはピンク枠の縦書きがいっぱい並んでいますが、ここが高度情報処理技術者と位置づけられてるところです。

試験名を見ればスペシャリストかゼネラリストかだいたい予想つきますね。

偉そうに話する前に、どれか持ってる?

3つ取りました。

ここから先行くならプロマネ、アナリストと進むところですが、私の目的と合致しないので受けてないです。

情報処理技術者試験は役に立たない?

そのまま参考書を勉強するだけではスキルアップにはなりません。そのうえ引用元にあるような暗記方式では尚更でしょう。

そのままではスキルアップにはなりませんが、技術者として幅を広げるという役に立ちますので、ここで私の過去を振り返ってみます。冒頭の「昔の自分を見ているような気がした」フラグ回収です。

昔の自分を見ているような気がした

引用元の匿名ダイアリーを見ると、昔の自分を見ているようです。

今にして思えば、昔の自分は視野が狭かったなぁとしみじみ思います。

私はもともと映像関係の組み込みソフトウェア開発をしており、何年か前にWEB系にキャリアチェンジしています。

そういった経歴のため、引用元にある以下の出題に関しては「知っているし、実装技術についても理解していて当たり前」という話になってきます。暗記するどころか勘ぐってしまいます。

次の画像符号化方式のうち,携帯電話などの低速回線用の動画像の符号化に用いられるものはどれか。
    JPEG
    MPEG-1
    MPEG-2
    MPEG-4

私の思考:

MPEG-4と答えさせたいのだろう。しかし、問題文が抽象的だなぁ。。。
ワンセグならMPEG-4でいいけど、「低速回線」というのがどのくらいを指しているのかがわからない。
いやいや、「回線」というからには放送関係の話ではなくスマホのHLSの話になるのかな。
そうすると、HLSで使用されるコーデックの話か?ああ、でもHLSの中身ってH.264だし実質MPEG4か。消去法で見たときもMPEG4以外なさそうだし。MPEG4でいっか。

といった感じです。

問題の解きやすさ/解きづらさというのは、所属する業界と立場によるものが大きくて、たとえば上記例題は業務システムの実装がメインの人は少々解きづらい問題です。(=手っ取り早く暗記で解決でもいいレベル)

これとは逆に、勉強を始めた当初の私はDB関係の出題は一切解けませんでした。 なぜならDBなんて触ったことありません。なのでERDなんて見たこともないし、書くこともない。タイミングチャート読めてロジアナでデバッグできるほうが重要。そういう世界です。

こういった所属業界や立場ごとの差があるのでこの設問はダメだとか頭ごなしに拒否するのではなくて、「知らない業界の話かな。。。」程度に軽く受け流してから、腰を据えて勉強するのがいいんじゃないかなと思います。

私もDBを勉強するために、当時のVisualStudioの付録のSqlServerを立てて色々動かしてやっと理解しました。すごく遠回りだったとは思いますが、身についた内容は今も使えているので、無駄にはならないと思います。

(物理マシン1台用意したのは勿体なかったかもしれない)

情報処理技術者試験が役立つ場面

先の例では業界や立場によって問題の解きやすさが変わってくるよという話でした。

そうすると情報処理技術者試験って誰向け?ということになるのですが、試験制度を再確認すると「基本情報」と「応用情報」は「情報処理技術者」としての土台に当たります。

即ち「全員これくらいは知っていてほしい」というレベルで、ソフトウェア開発に従事する人であれば、各個人どこに位置するかを調べるための腕試しということで良いのではと思っています。

「基本と応用がこんな簡単なんだから、高度も簡単に合格できるのでは」と思う人はまず問題集を買ってからざっと見てみるといいと思います。

たとえばエンベデッドであれば午前問題からいきなり回路図出てきたりしてなかなかカオスで楽しい世界です。2入力ANDを3入力ANDにしたいとか。

午後はウォシュレットの設計や立体駐車場の入出庫システム作ったりと、身近なシステム例だからこそ楽しかった記憶があります。

話を戻しましょう。

まあ、とりあえずは「役立つ/役立たない」論争になったときのために取っておくのをお勧めします。

試験に合格しておけば「持ってるけど実務じゃ役に立たないよ。転職の時の飾りかな」程度にドヤれるのでお勧めです。

真面目な話を少しだけすると、仕事の幅が広がるというメリットがあります。

試験に合格しておくことで

  • 何の知識もないし、あるかどうかも不明な人

という評価が

  • 最低限この試験の知識くらいはある人

に上がります。 そうすると仕事の内容も幅が広がっていきますし、その結果は転職でも有利に働きます。 その試験の難しさやレベルを理解しない会社であれば、辞めてもいいと思います。はい。 (ただし、情報処理技術者試験だけがすべてではないということをお忘れなく。)

情報処理技術者試験が役立つ場面(ネタ)

ネタになってしまいますが転職時に人身売買ブラック企業を回避できるメリットがあります。

実際にあったことなのですが、、、

面接時

面接官「基本情報とかの資格もってる?」
自分「基本情報は持っていませんが、ソフトウェア開発技術者とエンベデッドを持ってます」
面接官「基本情報持ってないの?うーん、あなたの年齢でそれはちょっと厳しいねぇ。。。クドクド」

とありがたい説教いただいたことがあります。

話の内容から推測するに、お客さんから「基本情報くらい持ってる人を寄越せ」くらいに怒られたのかな?と思っています。

帰宅するとその会社からお祈りの留守電入っていたので、すぐポチって消しておきました。嫌な事件だったね。

尚、このネタは業界辞めるまで使い続けるつもりです。

情報処理技術者試験の問題点(試験のやり方が古い)

技術内容に関する設問は少々古臭いとはいえ、汎用的なものを目指すとこうなるのだろうと思っています。

それはそれでいいのですが、やり方が古いと感じます。これが最大の問題です。未だに受験者を会場に集めて筆記式ですよ。 この筆記試験の最大の敵。それは高度試験の午後問題でやってきます。

いくつかの高度試験の午後2に「小論文の筆記」というものがあります。 これは「2時間ずっと文字を書く握力があるか」を調べる体力測定試験です。 合計2000~3000文字くらい(具体的な数字忘れました)をひたすら筆記するわけです。

私は体力不足とエンピツの持ち方が悪いのが相まって、2時間書き続けることができませんでした。

で、小論文の筆記って極悪で、章構成の変更が効かないんですよね。一度書いたらそれっきり。書き直ししていたら時間切れになってしまう。 なので、よくある文章の書き方

  • アウトラインで全体の構成を作る
  • どんな内容を盛り込むかをだいたいの肉付けしていく
  • 清書

を勉強して、書き慣れる必要があります。

当時は文章の書き方を勉強しつつ、体力づくりのために腕立て伏せとかしていたのですが、なんというか、もはや試験とは全く別のところで苦労していて「いい加減に試験制度を見直しなさい」と言いたくなるレベルです。

暗記で解けない午後問題

言及元ではこのように批判されています。

情報処理技術者試験で測れる能力は以下の2つだけです。

    内容の理解はともかく、ある用語を「聞いたことがある」かどうか。
    150分間、落ち着いて椅子に座っていられるかどうか。

まあ、午前試験についてはその通りでしょう。

しかしこれでは主語が大きすぎています。 情報処理技術者試験は前述のとおり、いくつもの受験カテゴリがあり、それぞれで試験内容が異なります。

内容の理解はともかく、ある用語を「聞いたことがある」かどうかだけで解けるのはせいぜい午前問題までです。

応用情報以上の午後およびそれ以上の問題は暗記や「聞いたことがある」レベルでは解けなくなります。

たとえば応用情報以上でよく見かけた設問にこんなのがあります

  1. A氏は下図のような設計を行った。しかし要件を満たせないことがわかった。満たせていない要件を要件定義シート中から抜き出して述べよ

  2. 前の設問で抜き出した要件を満たせていないのはなぜか、本来要件を満たすべきプロセス名をDFD中から抜き出して述べよ

  3. 実際に要件を満たすために、プロセスはどのように変更されるべきか述べよ

(疲れている状態で勉強していると「A氏に修正させろ。レビューで突き返せ」と投げたくなる、素晴らしい設問です。疲労度チェックに使えます)

このテの設問は用語や基礎や暗記だけでは解けませんし、現場独自のルールみたいなのも通用せず、一般論で解けるように作られています。

  • 文章をよく読んで理解し
  • 要件定義、設計書などから読み取れることだけを事実として
  • 「普通こうでしょう」みたいなローカルルールは持ち込まず
  • 一般的なやり方で、説明可能な手法で物事を解決する

ことが求められます。 これが結構難しくて、現場でも出来ない人が結構います。

何かしらの技術を勉強中の段階では特に顕著で、直観的な理解だけで済ませていると詰まってしまいます。 私自身もそういう部分があって、新しい技術要素などを「なんとなく理解したつもり」で使っていても、その内容の説明を求められて詰まることがあります。

このように「直観的になんとなく」ではなく「理論的に理解している」ことが試されるのが午後問題です。

感想と今後の展望

珍しく他人の文章に言及しました。昔の自分も言及先と似たようなことを思っていました。

たしか昔はこんなこと考えてたと思います。

  • こんなの役に立たないでしょ。うちの仕事じゃ使わない
  • マークシート式なので暗記ばかり。勉強が楽しくない。

コードを書いていくクリエイティブさとと比べると情報処理の試験勉強はとても地味で刺激が少ないものです。 突然出てくる未知の用語がとても苦痛だった記憶があります。

日本では試験に合格しなくてもプログラマとして働いていける環境なので、情報処理技術者試験は「将来の自分の選択肢を増やすための手段の一つ」として捉えておくくらいで良いかと思います。

なので、引用元の方の仰るとおり、役に立たないと思うならそれはそれで良いと思います。 利用価値の見えないものに時間を使うくらいなら、もっと費用対効果の高いことへ時間を使った方が幸せになれるということでしょう。

もし将来気が向いて「体系立ててITに関する勉強をしたい」とか思った時に選択肢の一つとしてあればいいかなと思います。

私も情報処理技術者試験に関してはシステムアーキテクトを取ってからはもう手を出すのを辞めました。(確かこのブログを開設したのと同じくらいの時期だったと記憶しています。)

今は自分で作りたいものをどんどん作っていき、必要があれば情報収集と勉強を繰り返していくフェーズだと思っています。

それでは今回はこれくらいで。

kerasを試す

この記事は以前下書きしたものを公開し忘れていたものです。

情報が古い恐れがありますのでご注意ください。

リモートワークでいつもと違う環境で仕事しています。かたりぃなです。 通勤時間がゼロなので、睡眠時間と勉強時間が増えていい感じです。 とはいえ自宅にはゲームなどの誘惑があるので、電源やLANを引っこ抜いておくなどの物理的対策を施しておく必要があります。

今回はDeepLearningフレームワークのKerasを試してみます。

動機

chainerの最終リリースが行われ、これが最終バージョンとなるそうです。 最新のDeepLearningを追いたい人はPyTorchへどうぞとのことです。

とはいえ、折角選んだchainerというフレームワークが終わってしまうので、そのままどうぞとガイドに従うのも少々嫌です。 ので、導入が簡単ときいたことのあるkerasを試してみます。

kerasはバックエンドを切り替えることができるらしいです。 一旦デフォルトのtensor flowで動かしてみて、慣れてきたらCNTK(Microsoft Cognitive toolkit)とかに変えて試したみたいなとか思っています。

環境構築

vscodeのコンテナ機能を使って簡単に試してみます

手元の環境は

です。

早速やっていきます。まずは実験用のコンテナを作ります。

  1. コマンドパレット(Ctrl + Shift + 'P')を開く
  2. コマンドパレットに'Remote'と入力。候補が表示される
  3. 表示された候補から'Remote-Containers: Open Workspace In Container'を選択
  4. 言語やフレームワークの選択で、Python3(Develop python3 applications)を選択する。

dockerfileのビルドが行われ、コンテナが起動します。 dockerfileやコンテナ起動時のパラメータ類は.devcontainerに格納されます

早速動かしてみましたが、ダメでしたので、設定変更しておきます。

コンテナの設定を変更する

pythonのバージョンを確認する KerasのバックエンドであるTensorFlowがpython3.7までしか対応していないとのことでした。(2020.02.28現在) バージョンを確認して、合ってないようなら修正します。

$ python -V
Python 3.8.1

はいダメですね。

dockerfileのfrom行で指定しているpythonのバージョンを細かく指示しておきましょう。

FROM python:3

FROM python:3.7

として保存します。

そしてvscodeを開きなおすと、「dockerfileが変更されているけどどうするか。rebuild or ignore」という選択肢が出るので、rebuildを選択してしばし待ちます。 暇なら"detail"をクリックしてログでも眺めておきましょう。

シェルが起動したら再度バージョンを確認。

$python -V
Python 3.7.6

よさそうです。 これでTensor Flowが入ります。あとでdockerfileに書くか、コミットしましょう。

apt-get update
pip3 install tensorflow
pip3 install keras

トラブルシューティング詳細

TensorFlowのPythonインターフェースを使うには、以下のurlにあるsystem requirementにあわせる必要があります ここにないバージョンの場合は修正する。(3.8 -> 3.7) https://www.tensorflow.org/install/pip

kerasの動作確認

サンプルコードで動作確認をします サンプルコードは

git clone https://github.com/keras-team/keras.git

で持ってこれます examplesに色々入ってる。 中身はchainerのやつと似たり寄ったりですね。

examplesの動かし方

cd keras/examples python ./mnist_mlp.py

ログ

Using TensorFlow backend.
2020-02-03 00:10:50.371771: W tensorflow/stream_executor/platform/default/dso_loader.cc:55] Could not load dynamic library 'libnvinfer.so.6'; dlerror: libnvinfer.so.6: cannot open shared object file: No such file or directory
2020-02-03 00:10:50.371973: W tensorflow/stream_executor/platform/default/dso_loader.cc:55] Could not load dynamic library 'libnvinfer_plugin.so.6'; dlerror: libnvinfer_plugin.so.6: cannot open shared object file: No such file or directory
2020-02-03 00:10:50.372012: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:30] Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.
Downloading data from https://s3.amazonaws.com/img-datasets/mnist.npz
11493376/11490434 [==============================] - 3s 0us/step
60000 train samples
10000 test samples
2020-02-03 00:10:55.722601: W tensorflow/stream_executor/platform/default/dso_loader.cc:55] Could not load dynamic library 'libcuda.so.1'; dlerror: libcuda.so.1: cannot open shared object file: No such file or directory
2020-02-03 00:10:55.722728: E tensorflow/stream_executor/cuda/cuda_driver.cc:351] failed call to cuInit: UNKNOWN ERROR (303)
2020-02-03 00:10:55.722776: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:156] kernel driver does not appear to be running on this host (2a25a8fd3a58): /proc/driver/nvidia/version does not exist
2020-02-03 00:10:55.723133: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2020-02-03 00:10:55.738064: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 2904000000 Hz
2020-02-03 00:10:55.738448: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x5591706d4e20 initialized for platform Host (this does not guarantee that XLA will be used). Devices:
2020-02-03 00:10:55.738608: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 512)               401920    
_________________________________________________________________
dropout_1 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 512)               262656    
_________________________________________________________________
dropout_2 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_3 (Dense)              (None, 10)                5130      
=================================================================
Total params: 669,706
Trainable params: 669,706
Non-trainable params: 0
_________________________________________________________________
Train on 60000 samples, validate on 10000 samples
Epoch 1/20
60000/60000 [==============================] - 7s 115us/step - loss: 0.2429 - accuracy: 0.9252 - val_loss: 0.1477 - val_accuracy: 0.9557
Epoch 2/20

ログをざっくり翻訳

mnistデータのダウンロードしてくれてますね。 ダウンロードしたデータセット~/.keras/datasets/に置かれるようです。

なんか警告がたくさん出てるけど、動かすだけなので今は無視します。NVidiaのDeepLearningSDKが入ってないとか、CUDAが入ってないとかそういうの。 cuInitでCUDA初期化失敗したり、カーネルモードドライバが無いと言ってたり。 さらに、CPUのSIMD命令を使おうとしたけどそれもダメでしたと(AVX2) 一言で言うと「学習すごく時間かかるよ」ってことですね。 GPUなし、CPUのSIMD拡張命令も無しってことなので。

ネットワーク確認

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 512)               401920    
_________________________________________________________________
dropout_1 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 512)               262656    
_________________________________________________________________
dropout_2 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_3 (Dense)              (None, 10)                5130      
=================================================================
Total params: 669,706
Trainable params: 669,706

コードでいうと以下の箇所と一致します。

model = Sequential()
model.add(Dense(512, activation='relu', input_shape=(784,)))
model.add(Dropout(0.2))
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(num_classes, activation='softmax'))

入門でよくあるmnistの3層のNNです。 Denseで生成される関数は output = activation(dot(input, kernel) + bias)とのことなので、そのまま解釈すればよさそうです。

  • dense_1,dense_2では512個のユニットがあり、
  • 活性化関数RELUを通して出力する
  • 念のため。dotはいわゆる内積。重みと入力を掛けて足す
  • ログ中のParamは各層の結合数 + ユニット数

ログ中のParamの値は、たとえばdense_1はMNISTの784次元のデータが入力されるので 784 * 512 + 512 = 401920 ということですね。 最初の512が重み係数をもつユニット数で、次の512がバイアスですね。 同様にdense_2は 512 * 512 + 512 で 262656となります。

感想と今後の展望

思ったより簡単に動きました。 マニュアルも普通に読めますし、当面はこれでいいかなといったところです。 それでは今回はこれくらいで。

2020年の抱負と昨年の振り返り

2019年のふり返り、2020年の抱負

新年あけましておめでとうございます。例年やっているふり返り記事になります。かたりぃなです。

まず最初に。気が付けばこのブログの読者も30人を超えていました。まだまだ未熟者のブログではありますが読んでくださってありがとうございます。

今後も活動を続けていきますのでご愛顧のほどよろしくお願いします。

最初に2020年の目標を一言で表現すると。。。

ダイの大冒険のアニメ化の合わせて、ARで魔法や必殺技を再現する!」

です。20年以上前の夢、メドローアやアバンストラッシュ撃ちたいとかミナカトールで魔法陣を発動させたいという夢を叶える絶好のチャンスです。

(いつも物体検出やトラッキングで詰まっているように見えるのは気のせいということにしておきましょう)

それでは2019年のふり返りと2020年の抱負をやってみたいと思います。

2019年やったこと

このブログで2019年に記事にしたものをふり返ってみます。

記事にしたものは3つあります。それとは別に、記事にしてないものも軽く触れてみます。

1. フォトグラメトリ

OpenMVGとOpenMVSですね。

目的は「コスプレ用のアイテムを3Dプリンタで作りたい。3Dモデリングが手間かかるので、3Dモデルも自動で作りたい」というものでした。

試行錯誤しましたが、目的を完全に果たすことはできませんでした。

目標達成には至りませんでしたがOpenMVGのコードを追うことで技術的な理解が深まった点は良かった点かなと思います。今までARで追ってきていた技術の基礎の延長上の話だったので、やっぱり基礎は大事だなと思った次第です。

最終的にはMeshRoomというOSSを使用することで、使えそうな3Dデータが得られました。このMeshRoomで生成した3Dモデルも完全なものではないので、3Dモデリングの下書きとして利用しています。

結局は単体技術で全ての課題を解決するだけではなくて、運用まで含めて妥当な落としどころを見つけるのが大事ですね。もちろん単体技術で解決できるならそれは理想ではあるのですが。

2. インフラ

私の中で利用できる技術が2つ増えました。

  • ELKスタック(ElasticSearch, LogStash, Kibana)
  • Ansible-Playbook + ELKスタック

どちらもdocker上で動かしています。

EKL

ログ分析はシステムを運用するうえで今後も必要な技術だと考えているので、少しずつでもこれらを使えるようになっておいて損はないと思います。

実際の運用ではELKを素のままで使うことはおそらくないだろうなと思っていて、何らかのデータストレージのフロントとして使うことが多いだろうと想定しています。

また、ELKに文字列を突っ込む際に形態素解析を試したりしたのも楽しかったですね。PythonからMecabを叩いてたりしました。

Ansible-Playbook + EKL

まだ記事にしていないお話しです。「Ansible-Playbook + ELKスタック」というものを試していました。

どういうものかというと、Ansible-Playbookのログ(changedとかokとか出るアレ)をELKに渡しておくことでログの視認性を良くするのが目的です。

Playbookのログって見づらいので、色々と試行錯誤していてこの手法に辿り着きました。

これができると何がいいかというと、よくあるPlaybookの問題「changedが出てるけど、これ何?常に出る(=実は無視していい)やつでは?」を簡単に切り分けできるようになるだとうと思っています。

もちろんログを蓄積してあるという前提ですが。

CI/CDの監視で使えば幸せになれるかもしれませんね。本年の早い段階で記事にしておくつもりです。

Ansible-Docker

これもまだ記事にできてないお話しです。Dockerのデプロイで外部のコンテナリポジトリを使わずにデプロイするものです。

AnsibleのモジュールでDockerコンテナを操作できるものがあるので、利用したものです。 やりたいことと完全にマッチしているわけではありませんが、工夫すれば色々とできそうです

Ansible-Playbookの枠組みで次のことが実現できるので、迅速に開始させたいサービスでコンテナを使う場合には選択肢としてアリなのではと思っています。

  • dockerイメージを作れる
  • dockerイメージをエクスポートできる
  • dockerイメージをインポートできる
  • コンテナ操作できる(調査中)

3. MixedReality(Hololens)

自分はHololensでC++を使う奇特な人種なので、UWP用の新しいC++/CLRとしてWinRTを試してみました。C++/CXより良くなってる気がします。しばらく使っていくうちに現時点での不足部分もわかってきました。

記事にしていませんが、C++/WinRTを使ってUnityネイティブプラグインを書いてみたりもしました。

まだうまくいってなくて落ちたりするので、もう少しデバッグしてから記事にする予定です。

Unityネイティブプラグインに手出しした理由は、C++だけだとライブラリの充実度などの問題で、開発速度があがらないためです。たとえばVRMやglTFのモデルの取り込みとかはUnityのほうが圧倒的に進んでいる印象です。

そう思ってUnityで色々と試してみたものの、やはり慣れない環境というのは手間取ってしまいます。

特にHoloToolkitからMRTKへの移行で躓いてしまったのは残念ポイントです。そうこうしているうちに2019年が終わってしまいました。

2020年はリベンジです。

あと新型も買わなきゃ(レンタルかなぁ)ですね。

4. 記事にしてない大きなカテゴリ

そのうち記事にするので軽く触れてみます。(後者は記事にはしないかも)

  • TweLiteプロジェクトの再構築
  • 投資

twelite

TweLiteはZigBeeプロトコルでIoT的な用途で使える便利なやつです。 今回TweLite2525Aが出てたのと、開発環境がVSCodeになっていたので一通り作り直しました。

久しぶりの組み込みプログラミングでしたが、慣れている部分が多いのでサクっといきました。 TweLiteは次の点が優れていると思います。

なので、私のような低レベルレイヤで戦った人であればコード読んですぐ開発に入れる環境でした。 (OS固有の仕様とか無いので、一般的な組み込みプログラムの理論だけで通用する)

電子回路回りも作ってあるので、あとはどこかのイベントで実験するだけですね。

投資

投資はプログラムとは全く関係なくて、他の道を探すときにどうなるのだろうという試みの一つになります。

動機は

  • 不労所得が欲しい(収入を増やしたい)
  • 老後の資産形成

などです。

サラリーマンの収入は、勤めている企業の収益や社内政治に影響される部分が大きくて、収入を増やすにはそれらの理解が欠かせません。

私は社内政治というか人づきあいに興味が薄いので、市場理解のほうへ挑戦したというところです。

市場理解と投資の関係について少し述べると、市場そのものの動きを理解するために実際に投資・運用して、自分自身が投資家の立場としてどうするかを理解していくのが近道だと考えたためです。

2018年は実験、2019年は本格的に投資の売買をしました。

一旦の結論として「物事を分析して判断する力」があれば投資で稼ぐことは可能だということがわかりました。

この「物事を分析して判断する力」力は多くのソフトウェアエンジニアには自然と備わっていると思っていて、例えばリスク管理の分野で考えるなら

  • どこまでなら損を許せるか(リスク評価と対策)
  • 損した原因を分析できる(原因分析)
  • スコープを明確化できる(短期的な売買/長期的な売買を区別できる)

などが該当するのではないかと考えます。 「損」という言葉を「障害」や「影響範囲」などに置き換えれば身近なものに感じるのではないでしょうか。

ひとまず年利5%前後を達成できているので、継続していきたいと思います。

ファイナンスのための確率解析 II (連続時間モデル)

ファイナンスのための確率解析 II (連続時間モデル)

ファイナンスのための確率解析 I

ファイナンスのための確率解析 I

  • 作者:
  • 出版社/メーカー: 丸善出版
  • 発売日: 2012/04/20
  • メディア: 単行本

こんな本購入したので、こちらも機械学習のついでに読み進めていっています。 知らない用語ばかりなので難航していますが、新しい知識というものは楽しいものです。

2020年のテクノロジー予想

ARって未来の技術」とか「特別なもの」みたいに思われていたのが2018,2019年までかなと思っています。

ここ数年のテクノロジーを見ていると、ARデバイス単体でARができるのは当たり前で、次のステップに移りつつあると考えます。

具体的には

などが考えられます。

ガートナーが発表しているテクノロジのハイプサイクルで2018年に「過度な期待のピーク期」を超えて「幻滅期」へ、2019年には図から消えました。2018年の発表では、"今後5~10年"に"競争優位性をもたらす可能性が高い"との表現でした。

このことから、2020年はまさに啓蒙活動や生産の安定性にむけた大事な時期ではないかと考えます。

xRについて思うこと。

毎年がVR元年と言われているここ数年ですが、エンドユーザーに向けてxR(VR,AR,MR)を推すのは少々筋が悪いのでは?と思っています。

xRは表現手段、情報の表示手段の一つであってユーザーが求めているものではないはずです。ユーザーが求めているものは

  • 必要としている情報を
  • 必要なタイミングで
  • わかりやすく見せてくれること

だと思っています。

スマートフォンが普及した現代ではスマートフォンの画面に情報を表示するだけで充分な場合もあります。 逆にスマートフォンの画面だけではわかりにくい情報は、別の方法でユーザーに見せる必要があります。

前者はいわゆるビューワーやクーポンなどのアプリで、後者は地図アプリなどが代表例でしょうか。

xRでも同じことで、概念的には「従来と見せ方の手段が異なる」だけです。

しかしデバイスとしての制約(装着するなど)があるため、どうしても区別してユーザーに説明することになってしまうので、「これはVRである」「これはARである」みたいにあまり重要ではない部分に話を持っていかれている感がします。

AR,MRについて技術的な観点から考えると「仮想世界と現実世界の情報をシームレスにやりとりする」という点がポイントになるのかなと思っています。

キーボードやマウスを使わなくても片手に収まるタッチパネルだけで情報検索ができるようになったのと同じように、タッチパネルを使わなくても声だけ、周囲の情報だけで自動的にユーザーが必要としている情報を推論して必要な情報を提示するという形が将来のMRなのかなと思っています。

2020年やりたいこと

Hololensをメインにやっていこうと思います。今までサーバ側がメインだったので、HololensとUnityをしっかりと調べたいと思います。

Hololens2も来ますし、そっちに軸足を移しつつ進めていきたいですね。

また、2020年からは転職も視野に入れて活動しようかと思っています。

本業は都内のWeb屋で働いていて、器用貧乏な中年です。 もしこのブログの内容を見て興味あるという方いらっしゃいましたらtwitterかコメントでも連絡いただければと思います。

本業の内容はこのブログより深く高度なことをやってはいますが、どろり濃厚なので公にはしづらいのです。あしからず。

それはさておき、まずは自分の力で何かを作り上げていくのが何よりも大事だと考えているので、今年もそれに向けて頑張っていこうと思います。それではこれくらいで。

本年もよろしくお願いします

VS Code Remote Developmentを試してみた(感想)

晩秋から冬へ移り変わっていく季節ですね。世間では色々なものが発表されていて、どこから手を付けていこうかとワクワクしています。 今回は開発環境まわりがパワーアップしたので、その記事になります。

開発環境の構築

Web開発ではリモートマシンで作業することが多く、そのためには数多くの設定をしてあげる必要があります。

俗に「環境構築」とか呼ばれる作業で、これがとても面倒だったりします。

実際の使い方は公式チュートリアルが充実しているので省略します。

https://code.visualstudio.com/remote-tutorials/containers/getting-started

自分の場合は既にdockerとvscodeが動作する環境があり、dockerもそれなりに触っていたので、1時間前後で完了して雰囲気は掴めました。

今回は少しだけ昔を振り返りつつ、今回のVScode拡張機能で何ができるかを纏めます。

ちょっと昔の環境構築

ちょっとだけ昔、Web開発といえばVirtualBox仮想マシン設定からでした。(今もある程度はこういう作業必要だったりしますが)

https://catalina1344.hatenablog.jp/entry/2014/04/02/222630

少し進んでVagrantとかPappertみたいな環境構築ツールも登場しましたが、環境構築の苦労はあまり変わらなかった印象です。

なんで大変なの?て言われると、その時々によって状況が変わるという部分が大きくて、たとえば

  • 環境構築ツール(たとえばVagrant)のバージョンが違う
  • 開発担当者の環境そのものが違う
    • win/mac
    • 環境構築ツールを動かす言語(RubyとかPythonとか)のバージョン

みたいなのがあります。

毎回こういう地雷を踏みながら、特定条件下(社内だけとか)でしか使えないナレッジが溜まっていくわけです。

最近の環境構築

いまどきはDockerという軽量でポータブルな仮想環境があるので、これで環境構築がとても楽になった印象です。

楽になった部分はありますが、まだ解決できていなかった部分もあります。

尚、今回のエントリは「まだちょっとだった部分」が解決されたよというお話しです。

dockerを使うことで昔より楽になった

  • ターミナル設定
  • ファイル共有設定
  • ネットワーク設定

dockerになってから、すごくラクになりましたね。 トラブルが起きたら結局設定ファイル見て頑張るみたいなのはありますが、それはそれで。

dockerを使っても、まだちょっとだった部分(今回のvscodeで解決!)

  • エディタの設定
  • デバッガの設定
  • localとremoteでのバージョン合わせ

ここはdocker含めて、環境構築ツールでは解消しきれていなかった部分です。

そもそもDockerが提供するのは「仮想環境」であって、「開発環境」ではないというだけな話です。

問題を解消できていない理由自体もとても簡単です。開発環境として使う場合、ローカルマシンとリモートマシン両方の設定が必要になってくるからですね。

たとえば

  • エディタをどう動かすか
    • localで動かしてファイル共有だけする
    • sshfsとsshで直にremote編集か
  • リモートデバッガでは接続先に何かをインストールする必要がある
    • 使う言語にあわせたリモートデバッガが必要
    • ネットワークのポート設定も必要だったり
  • intellisense(コード補完)を効かせるための設定が面倒
    • remoteとファイル共有だけしてるケースだと、localとremoteで同一パッケージが必要

みたいなのがあったりして、とても手間でした。

特に最後のintellisenseの設定が鬼門で、たとえばPythonで開発する場合

  1. DockerコンテナのPython関係のファイルをWindowsから見えるようマウントする
    • パスが違ってたりしてうまくいかない。
    • この方法は諦めた
  2. ローカルのWindowsマシンにAnacondaでも入れてみる力技でいってみる
  3. パッケージの関数も補完してほしいからpip installとかをwindowsとコンテナ側それぞれでやる
    • ただしWindowsには入れにくいパッケージがあったりする。(graphvizとか)
    • pip instal時にビルドするタイプ、かつ依存ライブラリもってるやつ
  4. windows側に全部そろっていくなら、dockerいらなくね?

みたくなってました。

VS Code Remote Developmentを使う

公式:

https://code.visualstudio.com/docs/remote/remote-overview

図を見るとわかるとおり、RemoteOS上にVS Code Serverを置いて、そいつが全部面倒みてくれるという仕組みのようです。

こうすると何が起きるのかというと、remotedevelopment当初のキャッチフレーズのとおり

VScodeとdockerコンテナのシームレスな連携」

です。

まずは使ってみた感想

控え目に言って最高です。

何が最高かって

  • ものの数分で各プログラム言語のコンテナが立ち上がって開発を始められる
    • js, php, python, C#, R, C++, ...
    • 裏でdocker-pullして必要なものを追加してくれてる
  • デバッガやコード補完も動く
    • 過去インストールした拡張パックが影響してるかもしれないが、特に難しい設定もなく動く
    • Python, PHP, C#(dotnet core)で確認
  • 追加のライブラリを入れたい
    • 単にdockerfileを編集すればいける
  • 開発環境の面倒な初期設定全部やってくれる
    • 以下のような問題を気にしなくていい
      • コンテナ作るためのdockerfileどうするとか
      • ネットワーク割り当てどうしよう、nginxの設定どうしようとか
      • ファイルのマウントどうしよう。どこが適切か(パーミッション維持する必要あるなら尚更)とか

少し掘り下げて使ってみた感想

少し掘り下げて、実用上問題ないか試してみた感想

  • よくあるWebアプリ(CMSとか)の基本構成もサポートされてる
    • Webアプリ + DB みたいな構成
    • python + postgreSQLみたいなのもプリセットにあるので楽々作れる。
  • 過去に自分が作ったコンテナを利用できる
    • dockerfile もしくは docker-compose.yml を指定するだけ
      • 起動時にどのコンテナにアタッチするか訊かれるので、アプリが動いてるコンテナを指定
    • 昔作ったdocker-composeで動作確認できた
      • nginxコンテナをfront
      • pythonPHPコンテナををback

実用を考えた場合にどうなのか?

VS Code Remote Development を実際に使うことを考えてみると、まあアリなのではないかなと思っています。

ただ、手放しで全てOKといえるわけではなくて、本番環境のコンテナどうやって作るかなどは別途考える必要がありそうです。

(開発用の色々なものが入ったままのコンテナを本番に置く勇気は無いので。。。)

とはいえ、原理的にはdockerを開発環境のback-endに置くというだけのことなので、dockerを前提とした開発であればアリだと思います。

構成もシンプルなので、困ったときは通常のdockerコマンドで外から操作できるのも強いところです。

感想と今後の展望

VS Code Remote Developmentという武器を手に入れたので個人開発が大きく捗りそうです。

デバッガの設定が面倒だからと諦めてprintfデバッグしてた環境とか、大きく改善されますね。

あとは開発環境の宗教論争が始まった時に「Windows10Proとdockerとvscodeで統一すればいい」と火に油を注げるようになります。

それでは今回はこれくらいで。