けものみち

まったりと、きのむくままに。

4.4万円するけど即完売するほど爆売れしている噂の HHKB Studio を購入して使ってみた!

adventar.org

これは タイパー Advent Calendar 2023 が大盛況だったので増枠していただいた タイパー(2枠目) Advent Calendar 2023 のトリを飾る記事になるはずが、執筆者の計画の無さが原因で大晦日に投稿された記事です。

HHKB Studio

happyhackingkb.com

2023年の10/25に発売された HHKB シリーズの最新版の製品です。

発売から2ヶ月経過した今も、再販と共に即完売してしまうほどの大人気高級キーボード(税込 44,000円)となっています。

今回は 12月頭の再販で無事購入に成功したので、3週間ほど使ってみた所感をまとめてみたいと思います!!

買う時は大胆に。お金のことは買ってから考えましょう。

到着!!!

HHKB の墨色は、程よいダークさに、適度な光沢感、硯のような美しさを感じることができて最高です。

キーボードの右斜め上側に置かれているのは、保証書や説明書が入っている封筒のようなものですが、招待状のようなパッケージです。素敵。

サイズ

サイズ感は Type-S と比べて、マウスキー・ジェスチャーパッドの分だけ一回り大きいくらいの変化です。

厚みはそこまで変わりませんが、少し増しています。

HHKB Professional シリーズで利用可能だった持ち運びケースでは HHKB Studio は運べませんでした。この点は注意が必要そうです。

MacBook Pro の上に載せるとこんな感じのサイズ感です。

キーボードの左上部から出ているプラグの部分だけ手前に寄せる必要がありますが、載せて作業しても違和感はないです。

配列

Type-S と物理的な配置は全く同じです。

HHKB Studio でも、Type-S 同様、キーマップ変更ツールが付属しています。 配列はプロファイルがいくつか用意されていて、デフォルトで Windows、プロファイル2に Mac 用の配列が登録されています。

試しに qwertyui に whitefox を割り当ててみましょう。

www.youtube.com

whitefox 配列が役に立つかどうかはさておき自由度の高いカスタマイズができるのは素晴らしいですね。

筆者の場合は、使わないキーによく使うショートカットキーを割り当てるといった実用的な使い方をすることになると思います。

背面

スイッチについては初期設定がほぼ最適ですが、必要に応じて機能を切ったり一部のキーの機能を変えることができます。Type-S とかではキーの機能を変化させることが多かったのに対し、Studio では新しく追加された機能のオンオフを切り替えるスイッチになっていて、キーの機能変化はキーマップ変更ツールに吸収されています。

傾き調整機能は今までの HHKB シリーズ同様、なし、小、大の3段階で切り替えられます。

Bluetooth

単三電池4本を召喚すると Bluetooth 接続することができます。私は基本有線接続で使用するので使っていませんが、線が邪魔、複数台繋ぎかえがよく発生するなどの理由で使いたい人にはいい機能ですね。ペアリングも最大4台まで可能です。

キースイッチ

静電容量無接点方式からメカニカルになったことで話題なので、ちょっと詳し目にレビューしてみます。 実際の打鍵感はお店で試打してみるとわかりやすいと思いますが、頑張って言語化してみたいと思います。

happyhackingkb.com

上記のサイトのキースイッチ特性に記載されていますが、既存の HHKB は三次関数のような荷重曲線を描いているので、キーの押し始めで少し強めの反発があり、真ん中まで押すとストっと落ち、一番下まで行くと反発が強くなるような感触です。クリック感(何かのスイッチを押す時のカチカチとした感じ)はありませんが、押し始めが少し重めに設定されているので、指にキーを押している感覚がちゃんと伝わってきます。

happyhackingkb.com

一方、HHKB Studio はリニアな荷重であるためか、体感かなりキーが軽く感じられます。指に負荷がかかる感じもなく、ほぼ力を入れずにストっとキーが落ちていく感覚が味わえます。自分はかなり好きです。

静音性も優れています。打鍵音はスコスコという感じで、低めの音が鳴るため、耳障りな感じは全くしません。

耐久性やチャタリングについての懸念はこれから継続して使ってみないとなんともいえませんが、打鍵感と打鍵音については想像より良好でした。

マウスキー

スペースキーの下に配置されている3つのキーです。初期設定では、左から順にマウスの左クリック、ホイール、右クリックに対応しています。 これがまたまた便利で、スペースキーを押すような感覚でマウスを操作できます。

次に紹介するポインティングデバイスと組み合わせて利用すると強力です。

ポインティングデバイス

GとHキーの間に挿入されているこの丸いデバイスです。指の力を入れた方向にマウスが動きます。

HHKB 以外で同じようなデバイス搭載しているキーボードを見た時は、使いにくそう、周辺のキーを打つ時に邪魔になりそうだと感じていました。 実際に使用してみると、最初こそデバイスの感度の調整や操作感に慣れるのが少し難しいですが、慣れるとマウスキーと組み合わせてキーボードから手を離さずに作業ができるようになり非常に便利です。

ラバー部分は取り外して掃除することも可能です。予備が数個備わっているので古くなったら取り替えてもOKです。

www.youtube.com

実際にポインティングデバイスとマウスキーを組み合わせて片手で操作してみた動画が上記のものになります。 マウスにいちいち手を伸ばさなくてよくなるので重宝しています。

ジェスチャーパッド

左、右、手前に2つの計4箇所に付いているデバイスです。見た目ではわかりにくいですが、実製品では反応する範囲に凹凸の突起がついています。 この領域に指を当ててスライドすることによって、キーマップ変更ツールで割り当てたショートカットを動作させることができます。

www.youtube.com

www.youtube.com

たとえばこんな感じで、ブラウザをスクロールしたり、Mac であれば立ち上げているアプリを指先一つですっと切り替えることができます。これでリモートワーク中でもスッとアプリを切り替えてタイピング練習をすることが可能になります。嬉しいですね。

感想

購入目的としては、どこにでも持ち運びできるキーボードが一台欲しかったというのと、エンジニアが本職なので、仕事での作業効率を上げることの二つが大きな目的でした。

購入後どのような変化があったかですが、キーボードから手を離す回数がかなり減りました。HHKB Studio のコンセプト通りでしょう。 SNS触る時、ブラウジング、仕事のコーディング、Slack でのやり取りで、キーボード+マウスキー+ポインティングデバイスの組み合わせで事足りるようになり、他のデバイスがほぼ不要になりました。

いままでは仕事の時は MacBook Pro のキーボードで打っていましたが、キーの深さがとにかく浅く、打ち心地もよいものではなく、かなり不満に感じていました。HHKB Studio を使うようになってからは、軽い感触で指に負荷がかかることなく気持ちよく打鍵できています。

この記事を書いている時点で、まだ3週間程度しか使用していないため、いまのところポインティングデバイスの感度以外はほぼデフォルト設定なのですが、キーマップ変更ツールで自分の仕事、作業にキー配列、ジェスチャーパッドを最適化させてみてより使いこなせるようになると、HHKB Studio の真価を発揮できるのではないかと感じています。

競技タイピングに向くかというと、ポインティングデバイスのラバー部分だけ取り外せば十分使用可能だと思います。ただし、昨今販売されているゲームに特化した多彩な機能が搭載されているキーボードを持っているのであれば、こちらをメインで使用する理由はないかと思います。HHKB Studio は、普段の作業を少ない手の動作で効率を向上させること、自分の作業に合わせて使いやすくカスタマイズしていくことが鍵になるキーボードだと思います。

総評としては、元々の目的は達成できたし、製品自体のクオリティも現時点で良いものと感じているので満足しています。

皆様も興味があれば少し勇気を出して購入してみてはいかがでしょうか。

Confusion Matrix を用いたタイピングの打鍵精度表現を考えてみる

概要

タイピングゲーム・練習サイトにおける打鍵の精度(正確さ、正確率など日本語の表記ゆれは多数あり)を混同行列(Confusion Matrix)を用いて表現してみました。

モチベーション

e-typing の全国平均データ

タイピングゲーム・練習サイトにおいて「精度」(Accuracy)といったとき、一般的には以下の式で算出されます。

\text{精度} = \dfrac{\text{正しく打った打鍵数}}{\text{総打鍵数}}

打鍵数ベースではなく、文字数ベースであれば、

\text{精度} = \dfrac{\text{正しく打った文字数}}{\text{総文字数}}

と算出されることでしょう。

この指標は、シンプルで理解しやすいのが利点です。一方で、何の文字をどれくらい間違えたかといった情報はすべて失われています。

そう考えると、「どの文字でミスタイプしたか」というのが気になってくると思います。

もっともシンプルな方法でミスタイプ数を計算するのであれば、入力すべき文字に対して違う文字が入力された場合、入力すべき文字のミスタイプ数を1増やしていくというのが考えられます。 例えば、a という文字を入力するべき時に、b を入力したとき、a のミスタイプ数を1増やします。

この方法はわかりやすいですが、出現頻度が異なる文字を単純な打鍵数ないし文字数で比較してよいのか、という点は気になるとは思います。

en.wikipedia.org

Wikipedia からの引用で、言語ごとにアルファベットの出現頻度を並べてみたものが掲載されています。例えば、E の出現頻度が高いとか顕著だと思います。 E がたくさん出てくるため、E のミスタイプ数が増えてしまうのは自然なことだとは思いますが、Eのミスタイプ数が多いからといって「Eが自分にとって苦手なキーである」と言い切れるかというと根拠に乏しいとは思います。

さらに、E を打つべきところでミスタイプしたとき、どのキーでミスタイプしているのか?という情報は欠落しているので、自分の打鍵の癖がわかりにくいというのももったいないように感じます。

さて、これらのことから、

  • 何の文字をどれくらい間違えたか見てみたい
  • 文字の出現頻度をある程度均等にならしたうえで苦手なキーを見てみたい
  • ミスタイプしたときどういうミスをする傾向があるのかを見てみたい

というモチベーションが出てきます。

混同行列(Confusion Matrix)

ja.wikipedia.org

混同行列についての詳しい説明は割愛します。難しくはないので適当にググって読んでみてください。

この混同行列をマルチクラスに拡張して、タイピングに応用してみましょう!

※以下の内容は難しければ、最後の「実験」まで読み飛ばしてもらって構いません。わかりやすくかみ砕いた説明を用意してあります。

定式化

文字の集合を $C$ とします。例えば、すべてのアルファベット小文字 "a"~"z" を取り扱う場合、

 C=\lbrace \text{"a", "b", "c", ..., "x", "y", "z"} \rbrace

といった感じになります(数学的にこの表記が正しいかどうかはさておき、文字が要素であることがわかればよいです)。

集合 $C$ の濃度(有限集合なので素数)を $|C|$ とあらわすことにしましょう。

さて、ここまで準備できれば、混同行列を作成することができます。具体的には、混同行列 M を $|C| \times |C|$ 行列とし、 $i$ 行 $j$ 列目の成分は、正しい文字 $c_i \in C$ に対してユーザが文字 $c_j \in C$ をタイプする確率 $p_{ij}$ で表すこととします。

 M = \begin{pmatrix} p_{11} & \cdots & p_{1j} & \cdots & p_{1|C|} \\
 \vdots & \ddots & \vdots & \ddots & \vdots \\
p_{i1} & \cdots & p_{ij} & \cdots & p_{i|C|} \\
 \vdots & \ddots & \vdots & \ddots & \vdots \\
p_{|C|1} & \cdots & p_{|C|j} & \cdots & p_{|C||C|} \end{pmatrix} \\
\displaystyle{
\sum_{j=1}^{|C|}{p_{ij}} = 1
}

初期値は精度100%としたいので、クロネッカーのデルタ  \delta_{ij} を用いると以下のようになります(要するに、初期値は単位行列です)。

 M = (\delta_{ij}) = \begin{pmatrix}
 1 & 0 & \cdots & 0 \\
 0 & 1 & \cdots & 0 \\
 \vdots & \vdots & \ddots & \vdots \\
 0 & 0 & \cdots & 1
\end{pmatrix} \\
\delta_{ij} = \begin{cases}
1 & (i = j) \\
0 & (i \ne j)
\end{cases}

実際の実装をするときは、 $|C|$ が大きくなるほどスパースな行列(つまり、成分が0となる箇所が多い行列)になりがちであることと、浮動小数点数での計算が面倒で誤差もあるので、打鍵数ベースで必要なデータだけを持つように実装します。 C# でクラス設計をすると例えば以下のようになります。文字が打たれるたびに打鍵数データを更新していきます。

public class TypingConfusionMatrix
{
    private static readonly string[] _characterSet =
    {
        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
        "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "-", ",", ".", "/", ";", ":", " "
    };

    private readonly Dictionary<string, Dictionary<string, int>> _countDataDictionary = new();

    public TypingConfusionMatrix()
    {
        // 確率1で初期化
        foreach (var character in _characterSet)
        {
            _countDataDictionary[character] = new Dictionary<string, int>() { { character, 1 } };
        }
    }

    public void UpdateConfusionMatrix(string correctCharacter, string inputCharacter)
    {
        // 文字列集合に含まれていない文字が渡された場合は処理しない
        if (!_characterSet.Contains(correctCharacter) || !_characterSet.Contains(inputCharacter))
        {
            Console.WriteLine($"文字列集合に含まれていない文字が渡されたので混同行列の更新は行いません(correctCharacter = {correctCharacter}, inputCharacter = {inputCharacter}");
            return;
        }

        if (!_countDataDictionary.TryGetValue(correctCharacter, out var currentCountData))
        {
            Console.Error.WriteLine($"文字列集合には文字 {correctCharacter} が含まれていますが、打鍵カウントデータがありません");
            return;
        }

        if (currentCountData.ContainsKey(inputCharacter))
        {
            var currentCount = currentCountData[inputCharacter];
            var newCount = currentCount + 1;
            currentCountData[inputCharacter] = newCount;
        }
        else
        {
            currentCountData.Add(inputCharacter, 1);
        }
    }

    public string DumpCountData()
    {
        var sb = new StringBuilder();
        const string header = "correct,input,count\n";
        sb.Append(header);

        foreach (var data in _countDataDictionary)
        {
            var correctCharacter = data.Key;

            foreach (var countData in data.Value)
            {
                var inputCharacter = countData.Key;
                var count = countData.Value;
                var dataString = $"{correctCharacter},{inputCharacter},{count}\n";
                sb.Append(dataString);
            }
        }

        return sb.ToString();
    }
}

実験

KIH2023 で自作した MimicTypeGenerator くんに打鍵ログをとる仕組みを忍ばせ、数分間適当に打鍵します。その後、打鍵データを dump します(ローカルなのでデプロイしていません)。

さて、dump した打鍵データ(混同行列)を jupyter notebook など適当に python の力をお借りしてヒートマップに可視化してみましょう。えいえいっ!

適当に数分打ったときのヒートマップ

$i$ 行 $j$ 列目の成分は、正しい文字 $c_i \in C$ に対してユーザが文字 $c_j \in C$ をタイプする確率 $p_{ij}$ でした。 今、ヒートマップが赤いほど確率が1に近く、青いほど確率が0に近いので、対角成分が赤いということは精度が高いということを表しています。

下から7行目の Hyphen の行の対角成分だけ黄緑色ですね。これはハイフン(-)を打つ時の精度が低いことを表しており、実際成分をみると 0.57 と書いてあるのでハイフンは 57% しか正しく打てていないことがわかります。また、Hyphen を打つときに、e、i、r、uが青くなっていることから打ち損じがある可能性、0を押していることから隣のキーを押してしまっている可能性が推測できます。どうやら私はハイフンが結構苦手なようです。

上記のデータはまあまあ精度高く打ってしまったので、もっとスピードを上げてミスりまくってみましょう。

対角成分が橙色の行はやや精度が低い行です。例えば m の行は対角成分が 0.81 なので m の精度が 81% で、そのときほかのキーにまばらに0以外の数値が入っているので、mの打ち損じが多かった可能性が推測できます。

さらに、乱打してみましょう。手動で乱打するのはめんどくさいので、10万打鍵適当な文字を打つスクリプトを書いて回しました。自作サイトでローカル環境なのでいくら雑に荒らしても全く問題がありません。

全体的に青くなりましたね。よさそうです。

対角成分が1になっている部分がところどころあるのは、課題文章にその文字を打つケースが出てこなかった、その文字が正解だとしてもその文字を打たなかったことが想定されます。例えば q とか数字とか出てなさそうですね。

メリット・デメリット

メリット

  • 文字の集合さえ定義されていれば計算可能
  • 何の文字をどれくらいの確率でどのように間違えるかが可視化できる
  • 文字の出現頻度を考慮できている

文字ごとの精度、どう打ち間違えたかが可視化されるのでより客観的な分析ができそうな気がしますね。

デメリット

  • 正確なデータを取るには相当打鍵数が必要
    • 特に出現頻度が低いものはなかなかデータが取れない
  • 文字の集合の要素が増えるとデータ取り直しになる(文字の集合が固定されている必要性がある)
  • 解釈が難しい
    • 対角成分以外の小さい値をどう評価するかが難しい

やるまえから予測できてはいましたが、数千打鍵程度だと全然特徴が表れてこないので、たくさん打ってデータを蓄積する必要があります。 しかも、文字の種類が増えるほど行列がスパースになるし、対角成分以外の値が小さくなりがちで評価が難しくなる傾向にあります。

終わりに

タイピングにおける「精度」という概念を、混同行列という方法を用いて表現してみました。応用可能性はあると思うのでぜひアレンジして試してみてください。