DOSEIの日記

技術メモ+日常ログ

面接官「特技はR-CNNによる肖像画検出とありますが?」

KTGW君 「はい。紙幣の肖像画検出です。」

面接官「R-CNNとは何のことですか?」

KTGW君 「Regions with CNN の略で、領域候補をディープラーニングで求めます。」

面接官「え、ディープラーニング?」

KTGW君 「はい。ディープラーニングです。畳み込みニューラルネットワークに大量のデータを与えて識別されるものを訓練します。」

面接官「・・・で、その肖像画検出は当社において働くうえで何のメリットがあるとお考えですか?」

KTGW君 「はい。国外の未知の紙幣を少ない手間で識別できます。」

面接官「いや、当社には識別したい紙幣などありません。それに紙幣を国外に持ち出すのは違法なことが多いですよね。」

KTGW君 「でも、スライディング窓にも勝てますよ。」

面接官「いや、勝つとかそういう問題じゃなくてですね・・・」

KTGW君 「NMS かければそこそこの精度にはなりますけどね。」

面接官「ふざけないでください。それにNMSって何ですか。だいたい・・・」

KTGW君 「Non-maximum suppression です。非最大値抑制とも言います。抑制というのは・・・」

面接官「聞いてません。帰って下さい。」

KTGW君 「あれあれ?怒らせていいんですか?使いますよ。VGG16。」

面接官「いいですよ。使って下さい。VGG16とやらを。それで満足したら帰って下さい。」

KTGW君 「運がよかったな。今日は fine tuning が足りないみたいだ。」

面接官「正例少ないよ。」

Range based for の為の組み合わせ生成イテレータ

以下の例は昔のバージョンです。最新版は github に置いてみました。

github.com

実験で、全組合せに対して繰り返し処理をしたい時に使う。

先に使用例。 C++11 以降で使える range-based-for 文を使っているので、 gcc なら -std=c++11 (or -std=c++0x) が必要。

#include <iostream>
#include <vector>

#include "combinations.hpp"  // この記事の下に実装があります。

void printVector(std::vector<size_t> const& v)
{
    for(size_t const i : v)  std::cout << i << ' ';
    std::cout << std::endl;
}

int main()
{
    Combinations const C(5,3);

    std::cout << "By iterator" << std::endl;
    for(auto it = C.begin(); it != C.end(); ++it)
        printVector(*it);

    std::cout << "By range-based-for" << std::endl;
    for(auto const& v : C)
        printVector(v);
}

出力

By iterator
0 1 2 
0 1 3 
0 1 4 
0 2 3 
0 2 4 
0 3 4 
1 2 3 
1 2 4 
1 3 4 
2 3 4 
By range-based-for
0 1 2 
0 1 3 
0 1 4 
0 2 3 
0 2 4 
0 3 4 
1 2 3 
1 2 4 
1 3 4 
2 3 4 

コード (ヘッダオンリー) combinations.hpp。 割と適当なので、間違ってたら誰かが直してくれるはず(おながいします)。 C++11 以降で使える iota を使っているので、 C++98 なら適宜修正のこと(つたわれ)。

#ifndef COMBINATIONS_HPP_INCLUDED
#define COMBINATIONS_HPP_INCLUDED
#include <iterator>
#include <vector>
#include <algorithm>
#include <cassert>

// n から k 個とった組み合わせを生成する.
// イテレータに値を生成を任せて、本体は値を保持しない.
class Combinations
{
    typedef std::vector<size_t> CombinationType;
public:
    Combinations(size_t n, size_t k) : n_(n), k_(k), initial_(Sequence(k)) { };

    class iterator : public std::iterator<std::input_iterator_tag, CombinationType>
    {
        public:
            iterator(size_t n, CombinationType const& seq) : n_(n), seq_(seq) { }
            iterator(const iterator& it) : n_(it.n_), seq_(it.seq_)  { }

            iterator& operator++() 
            {
                if(seq_[0] == n_ - seq_.size())  seq_.clear();
                else  next();
                return *this;
            }
            iterator operator++(int) { iterator tmp(*this); operator++(); return tmp; }
            bool operator==(iterator const& rhs) const { return seq_ == rhs.seq_; }
            bool operator!=(iterator const& rhs) const { return seq_ != rhs.seq_; }
            CombinationType operator*() const { return seq_; }

        private:
            iterator() : n_(0) { }; // forbidden


            size_t const n_;
            CombinationType seq_;

            // seq_ は真単調増加列であるとする.
            void next()
            {
                assert(seq_[0] < (n_ - seq_.size()));
                size_t const k = seq_.size();
                // 配列の後ろから i 番目を順にみて行き、
                // 繰り上がりが発生しなくなるまで繰り返す
                for(size_t i = 0; i < k; ++i)
                {
                    // 注目している位置
                    size_t const idx = (k - 1) - i;

                    size_t const vv = seq_[idx];
                    assert(vv < n_);

                    // 最大値に達していなければ、
                    // 1 増やして、以降を連番で埋めて、終了
                    if(vv != (n_ - 1) - i) 
                    {
                        for(size_t j = 0; idx+j < k; ++j) seq_[idx+j] = (vv+1) + j;

                        break;
                    }
                }
            }
    };

    iterator begin() const { return iterator(n_, Sequence(k_)); }
    iterator end() const { return iterator(n_, CombinationType()); }

    private:
        size_t const n_, k_;
        CombinationType const initial_;

        static CombinationType Sequence(size_t n)
        {
            CombinationType I(n);
            std::iota(I.begin(), I.end(), 0);
            return I;
        }
};

#endif // COMBINATIONS_HPP_INCLUDED

TODO:

std::vector<std::string> const words = { "Alpha", "Beta", "Gamma", "Delta", "Epsilon" };
Combinations C(words, 3);

とかできたらいいよね。

iBus 1.5 の日本語入力

とりあえず ibus 1.5 でググってから読むといいと思います。

Fedora 20 あたりから、日本語入力が iBus 1.5 になった。クリーンインストールした直後だと、日本語入力を行うために Super-space で切り替える。

コンパネの「地域と言語」をみると、初期状態で「日本語」と「日本語 (かな漢字)」が登録されている。 Super-space ではこれらを切り替える。キーバインドは「キーボード→ショートカット」の「タイピング」で指定できる。

初期状態は「日本語」が選択されていて、これは単に日本語キーボード配列で英数字を入力できるだけである。直接入力専用の IME と思ってもよい。一方「日本語 (かな漢字)」は kkc という IME であり、初期入力モードは「ひらがな」(漢字変換をするモード)である。

今まで普通の人は半角/全角キーで、 IME のモード「ひらがな」と「直接入力」を切り替えていた。らしいしが、私は ctrl-space で IME 自体の起動・終了をしていたので、それはいいと思う。

問題は、切り替えが重い。一瞬 IME の選択画面がオーバーレイして出てくる(mac みたいに)のだが、これに時間がかかる。あと、一瞬フォーカスが奪われるような動作になっている。

今までの普通の人の状態を再現するには、「日本語」をけして「日本語 (かな漢字)」だけにすればよさそうなのだが、なぜかうまくいかない。起動直後の入力モードが「ひらがな」だと都合が悪いので「直接入力」に設定できるが、起動直後に「あ」とインジケータが表示されている。まぁ、直接入力はできるが、デフォルトで設定されているモード切替のキーを打っても切り替わらない。なぜなんだぜ。

最大の問題は、初期状態で「半角全角キー」で日本語切替できないこと。じゃあ、 super-space と同じようなキーを追加すればいいかというと、キーは一つしか指定できないらしい。 super-space を捨てて Zenkaku Hankaku キーを指定すればいいかというと、これだとなぜか切り替えがうまくできない。というわけで、現状はあきらめるしかない?

Ctrl-space に指定した場合は、gnome tweak tool の「ポインタの位置をハイライト」機能をオンにすると、 ctrl の動作を奪われるのでオフにすべし。

なお、英語キーボードの人はどうすればいいかというと、「日本語」「日本語 (かな漢字)」の組を「英語」「日本語 (かな漢字)」に変える。どうやら「日本語 (かな漢字)」のほうは直前のキーボードレイアウトに従うらしい。変な仕様。

まとめ: Fedora 20 などで初期設定での日本語入力は Super-space で切り替える(か、ショートカットを ctrl-space などに変える)。半角/全角キーでの切り替えはできない。

余談:キーボードの刻印は「半角/全角」だけど、 Gnome の表記は Zenkaku Hankaku だし、俺もそうだと思ってた。

Fedora 19 の TeX 環境

Fedora 19 で日本語の TeX 環境を入れるには、少なくとも texlive-ptex を入れる必要がある。 texlive-japanesetexlive-cjk では、なぜか ptex が入らない。もし、 unicode 対応の uptex, uplatex を使う場合は、 texlive-uptex をいれる。

だが、これだけだとうまく動かない。さらに追加で、 texlive-texconfig, texlive-metafont, texlive-cm が必要となる。で、足りないものをちまちま足していくのは大変なので、容量が許せば texlive パッケージをドーンと入れるのがよいと思われる。 (texlive-ptex は別途必要)

dvidpfmx は texlive-dvipdfmx で入る (dvipdfmx パッケージでは不完全)

なんか、 Fedora 18 まではもっとうまく導入できた気がするんだがなぁ。