meowの覚え書き

write to think, create to understand

地理的位置に基づく店舗名の(エセ)AND検索

画像引用元: https://tabelog.com/hiroshima/A3401/A340121/34000922/dtlphotolst/3/smp2/

この記事では、↑の写真の撮影場所の特定に挑戦した際に考案した手法について記す。具体的には、店舗名2つを指定すると距離の近い店舗ペアを列挙する手法を実装し、スターバックスコーヒーとFrancFrancが隣接する建物の特定を試みた。

先に述べておくと紹介する手法では場所の特定はできなかった。しかし、今後撮影場所の特定を行う際に流用できる可能性がある。


スポンサードリンク

続きを読む

音声認識エンジン『Julius』を使って韻を踏んだフレーズペアを探す

f:id:meow_memow:20211231223546j:plain

こちらは『創作+機械学習 Advent Calendar 20212枚目の2日目の記事です。

adventar.org

先に記事の要旨を説明すると、音声認識器の認識結果を敢えて複数個出力することで韻の近い単語1列(以下フレーズと呼ぶ)を検索するツールとして使えないか検証したという内容です。簡易的に実験した結果、狙ってフレーズを検索するのは難しいですが、面白いフレーズペアを偶発的に発見する用途には使えそうという印象です。

背景

作詞の現場で、押韻を用いた表現を探したい(韻を踏みたい)ケースがあります。例えば「思い出」の母音「o o i e」に近い言葉を探すと「遠い目」という単語が使えそうです。

語彙力が豊富な方やラッパーをなりわいとしている方であれば瞬時に言葉の候補が思い浮かぶのかもしれませんが、経験・スキル・センスが問われる作業だと思います。

そこで誰でも再現できる手順で、似たような音の響きを持つ言葉を探す方法を考えます。

既存手法と課題

考えうる方法としては2つあります。

既存手法1: 過去事例の流用

既存手法の1つめは過去事例の流用です。これはラップの歌詞やMCバトルの書き起こしから使えそうなものをストックしておくというものです。
過去事例の情報源ですが、例えば『韻ノート』には、ラッパーの踏んだ韻を含むラインが第三者により登録されていることがあります。

in-note.com

例えば”R-指定”というラッパーだと次のようなものがあります。

「しかと見ときな格の違い キッズとキングの箔の違い お前殺すぜ覚悟しな ここに転がるカスの死体

https://in-note.com/rhyme_groups/12

これならば、プロが踏んだ質の良い韻を使うことができそうです。
しかし、この方法で自分の作品を作りたいと思う人はいないと思います。質が良いということはオリジナリティも高く、やみくもに使うと”他人のパクリ”とみなされるためです。作詞作業に限らず創作界隈においてはご法度でしょう。
したがって、他人が踏んでないような韻を探す必要があります。

既存手法2: 母音に基づく検索

既存手法の2つめは母音に基づく検索方法です。
インターネット上には母音で引ける辞書サービスがあります。

kujirahand.com

たとえば「思い出」と踏みたい韻を探すために、母音“おおいえ”で検索すると、「乙姫」、「とこしえ」というものがヒットします。 また、「五ノ池」のような固有名詞もヒットします。「おおいえ」を母音とする単語単体だけでなく、さらに「おおいえ」を前後に含む単語を検索することができます。

また、"背景"の項目で紹介した「韻ノート」ならば、母音に直さずとも、韻を踏みたい単語で探せます。
試しに「思い出」で検索すると、「暴行事件」「東京事変」などという結果が提示されます。「っ(促音)」や「ー(長音)」を考慮した検索ができるので、現実の発声に即した検索ができます。

既存手法の課題

上に挙げたツールは作詞作業には十分使える一方で限界も2つあると考えます。
1つ目は基本的に1単語対フレーズ(もしくはフレーズ対1単語)でしかヒットしないことです。フレーズ対フレーズではどのようなものがあるかといえば例えば、 『夕方ノスタルジー / ZORN Feat. WEEDY』では「ラムネ の 瓶」と「鳴る メロディ」という韻を踏んでいます。3単語対2単語ですし、単語の切れ目も一致していないので先程の検索ツールではヒットしないでしょう。
2つ目はツールは母音は完全一致のみヒットすることです。 途中に1文字程度の母音が多めに入っていたり抜けていたりというものはヒットしないです。前述の『夕方ノスタルジー / ZORN Feat. WEEDY』では「賃貸マンション」と「心配無いよ」で踏んでいる箇所があります。母音こそ完全一致ではないですが、歌詞の文脈とも合わさって全く違和感がありません。

以上から、既存手法には課題は3点あると考えます。

  • オリジナリティが発生しない程度に一般的かつ新規性がありそうなものを探すこと
  • フレーズ同士の組み合わせを探すこと
  • 音が完全一致でないものも探すこと

提案手法

この課題3つを音声認識エンジンJuliusを使うことで同時に解決できないかと考えました。

julius.osdn.jp

核となるアイディアは「Juliusに実装されている、似たような音の響きを持つ単語の候補を時間区間ごとに出す機能を使う」ことです。具体的には下図の"単語グラフ"のようなイメージです。

f:id:meow_memow:20211231224528j:plain

図: 公式ページの"単語グラフ出力"より引用

このような単語の候補のグラフから、韻を踏んだフレーズペアを探すというものです。

Juliusの音声認識アルゴリズム

Juliusを使うとなぜアイディアが実現できるのか、Juliusで採用されている音声認識アルゴリズムの大まかな仕組みを知っていた方が理解しやすいと思うので説明します。(とはいえ他人に説明できるほど音声認識を理解できていないのでやんわりとですが...)

ここで覚えておいていただきたいことはJuliusの音声認識は内部で音響(何と発音されているか)と言語らしさ(言葉としての現れやすさ)をスコア付けするグラフ探索問題を解いているということです。

f:id:meow_memow:20211231224734p:plain

図: Juliusの論文より引用

  1. 特徴抽出: 入力されたディジタル音声信号から一定時間長で識別のための特徴量を連続的に抽出していきます。JuliusではMFCCという特徴量を抽出します。
  2. 音素系列の認識: 処理1で抽出した特徴量系列を音響モデル(図で音素モデルと呼んでいるもの)により、音の並びである音素2列を推定します。具体的な処理としては、音素ごとにHMMが用意されており、観測された特徴ベクトルを生成する尤度を各HMMが計算しスコアを算出します。
  3. 音素列から単語に直す: 音素列は発音辞書を用いて音素列に該当する単語が存在するかをチェックします。
    1. 発音辞書は次のようなものです: https://github.com/julius-speech/dictation-kit/blob/master/model/lang_m/bccwj.60k.htkdic
    2. もちろん「a sh i t a」を「明日」とするのか「足 田」とするかという複数の変換先がありますが、そこは手順4で確率的に決めます。
  4. 単語列の言語的確率を付与: ここまでで、音素列が単語列へと変換されていますので、言語モデルを用いて単語列に言語的観点から単語列の生成確率(スコア)を与えます。例えば「今日 の 天気」と「京都 天気」だと前者の方が確率が高く与えられます。
  5. 手順2〜4は、どのHMMから通ってきたか、どういう単語に直したかなどで探索経路が異なるため、複数のパスを候補として持ちながら探索を進めます。
  6. パスの候補の中から一番音響モデルが出したスコアと言語モデルが出したスコアの高いものを音声認識結果として提示します。

Juliusでは2パス探索を採用しており、手順2〜4を2回行います。1パスめでは始点から終点へ順方向に計算処理の軽いモデルでスコアを求めます。2パスめではより前後の文脈に応じたモデルで終点から始点へ逆方向に探索します。このとき1パス目の情報(どの時刻にどういう単語がどういうスコアであったか)をヒューリスティックとして用いることで高速に探索します。

アイディアの再掲

Juliusは音の近さと言語らしさを考慮してグラフの探索問題を解いているという点を説明しました。また、複数の経路を探索候補として保持していることや、いつの時刻に何の単語がありそうかという内部表現を保持している旨を述べました。

ということは、自分が検索したい韻を踏んだフレーズをJuliusに認識させれば、複数の認識結果やJuliusの内部表現から、音の聞こえ方が似ていて日本語としても自然な表現を集めることができるのではないかと思いました。

このアイディアを実現する方法には至ってシンプルで「Juliusの内部表現を出力するオプション3をつけて、踏みたい韻を含むフレーズを発声し、表示された結果をメモする」ことです。

Juliusで実際にフレーズペアを探す

Juliusのセットアップ

大変ありがたいことに、Juliusをすぐに試験できるように”ディクテーションキット”が公式的に用意されています。

julius.osdn.jp

”ディクテーションキット”には各OSごとのJuliusのバイナリと、日本語音声認識のためのリソース(学習済みの音響モデルと言語モデルなど)が既に同梱されており、コマンドラインからバッチを実行すればすぐに音声認識を開始することができます。

音声入力手段はデフォルトではマイク入力ですが、実行オプション(-input file)で音声データ4を指定することも可能です。

実験

認識結果上位を表示するように出力

Windows10でrun-win-gmm.batをオプションをつけながら実行してみました。

まず、自分が今踏みたい韻のフレーズを含んだセリフを音声認識し、認識のスコアの上位10つを出力してみます。 ここでは試しに「いい 思い出」に近い聞こえ方のフレーズを探してみます。

「あれはいい思い出だった」と発話した結果を記します5

sentence1:  あれ は いい 思い出 で 、 だ 。
sentence2:  あれ は いい 思い出 って 、 だ 。
sentence3:  あれ は 入り 思い出 って 、 だ 。
sentence4:  、 あれ は いい 思い出 で 、 だ 。
sentence5:  あれ は 入 思い出 で 、 だ 。
sentence6:  、 あれ は いい 思い出 って 、 だ 。
sentence7:  R Y いい 思い出 で 、 だ 。
sentence8:  あ 、 あれ は いい 思い出 で 、 だ 。
sentence9:  R Y いい 思い出 って 、 だ 。
sentence10:  あ 、 あれ は いい 思い出 って 、 だ 。

本来見つけたかった「いい 思い出」の部分はどの結果でも正しく認識されており、フレーズペアを見つけることに失敗しました。敢えて言うなら「あれ は いい」と「R Y」がフレーズ対フレーズのペアなのですが、歌詞として使えるケースは極めて限定的でしょう。

”単語ラティス形式”で出力

複数の認識結果を見ることは目が疲れるので、”単語ラティス形式”で出力(オプション-latticeをつけて実行)をしてみました。こちらは、どの区間にどのような単語がありそうかを可視化できるものです。

先程と同じく「あれはいい思い出だった」と発話した結果を記します。

-------------------------- begin wordgraph show -------------------------
   0:
   0:|-|
   1:
   0:|------|
   2:
   0:|-------|
   3:   ああ
   7:  ||
   4:   あ
   7:  ||
   5:   あー
   7:  ||
   6:   、
   7:  |-----|
   7:     、
  10:    |---|
   8:         タレ
  20:        |--------|
   9:         たれ
  20:        |--------|
  10:         多衣
  20:        |--------|
  11:         佐江
  20:        |--------|
  12:         幸い
  20:        |-----------------|
  13:         は
  21:        |---|
  14:         彼
  21:        |-------|
  15:         あれ
  21:        |--------|
  16:         彼
  21:        |--------|
  17:         かれ
  21:        |--------|
  18:         カレー
  21:        |--------|
  19:         我
  21:        |--------|
  20:         われ
  21:        |--------|
  21:         姉
  21:        |--------|
  22:         吾
  21:        |--------|
  23:         金
  21:        |--------|
  24:         腫れ
  21:        |--------|
  25:         あれ
  21:        |--------|
  26:         晴
  21:        |--------|
  27:         綾
  21:        |---------|
  28:         彩
  21:        |---------|
  29:         亜矢
  21:        |---------|
  30:         アレン
  21:        |----------|
  31:         カレン
  21:        |----------|
  32:         亜鉛
  21:        |----------|
  33:         か弱い
  21:        |----------------------|
  34:             良い
  32:            |------------------|
  35:                  の
  43:                 |------|
  36:                  は
  44:                 |------|
  37:                   は
  47:                  |-----|
  38:                    は
  49:                   |----|
  39:                         いい
  62:                        |------|
  40:                           いい
  66:                          |----|
  41:                                思い出
  79:                               |----------------|
  42:                                                 だっ
 122:                                                |-----------|
  43:                                                             た
 151:                                                            |-----|
  44:                                                                    。
 168:                                                                   |-|
-------------------------- end wordgraph show ---------------------------

やはり「いい 思い出」の部分はまとめられてしまっています。しかし、「あれ は いい」の部分に「か弱い」という表現が出てきました。母音が完全一致していないですが似たような響きの単語が偶発的に得られたと思います。

考察

自分が見つけたい韻のフレーズをピンポイントでみつけることは難しそうです。それは尤もなことで、認識結果の上位に来るものは当然似た認識結果になりやすいです。もっとスコア付けをオプションで調整し、「聞き間違え」を意図的に誘発して認識結果の多様性を上げる必要がありそうです。

このような理由から、フレーズの検索用途というよりも、何らかの音声を”単語ラティス形式”形式で垂れ流しておき、作詞に使えそうなフレーズペアを偶発的に発見してそれをストックする用途の方が適しているのではないかと思います。

私の手法のその他の限界としてはJuliusの学習モデルの制約が考えられます。例えば語彙は bccwj.60k.htkdic 内の単語に制限されています。このファイルに単語を追加することもできますが、言語モデルには含まれていないため、未知語の確率が割り振られ認識結果に出る確率が下がり認識結果としてヒットしにくいです。また、音素の連節(トライフォン)もlogicalTriというファイルで定義されており、このファイルに載っていない音素列を含む単語は登録することができません。このように、キットを改造するには音声認識への理解やエンジニアリングが必要で高い技術力が要求されそうです。

まとめ

音声認識エンジンJuliusを用いて、フレーズ同士の韻を踏んだ表現を検索(あるいは収集)する方法について提案しました。

探したい韻のフレーズを検索する用途に用いるのは必ずしも成功しないですが、実際に作詞に使えそうなネタを偶発的に発見する用途としては使えそうであることがわかりました。もちろん作詞活動だけでなく漫才の聞き間違えネタに使えそうで、コメディと相性がよさそうです6

今回は音声認識モデルでしたが、他のタスクも同様に認識結果のうち次候補以降や推論ミスも面白い用途に使えるかもしれません。

それでは、最後まで記事を読んでいただきありがとうございました。

追記:
再掲になりますが、『創作+機械学習 Advent Calendar 2021』では、創作活動用途に機械学習技術を適用する取り組みを有志が多数公開してくださっています。どの記事も面白いので、ぜひラインナップを一読いただければと思います。

adventar.org

参考文献


  1. ここでは形態素を想定しています。

  2. 音声言語を構成する最小単位です。

  3. この中間結果出力機能があることが、数ある音声認識ライブラリの中からJuliusを選んだ理由です。

  4. サンプリングレートは16kである必要があります。

  5. airpodsのマイクで入力しているため、認識結果は悪いです。

  6. 機動戦士ガンダム 閃光のハサウェイ』冒頭事前公開でも音声認識誤りが発生し低俗な字幕が表示されたために、話題になりました。

【VSCode】Pythonコードのデバッグ中に画像の入った変数を可視化できる拡張機能を試した

はじめに

この記事では、Visual Studio Code(以下VSCode)の拡張機能の1つを使った際の所感を紹介する。

私は普段、画像系の機械学習タスクに取り組む時VSCodeで開発している。 その際、画像に対して自前実装の前処理やデータオーグメンテーションがちゃんと施されているかを簡易的に確認したい場合がある。

そういう時に私はこれまでディスクに書き出して結果を確認していた1:。 しかし、もう少しスマートな方法がないかと思い、デバッグ中に画像を可視化できるVSCode拡張機能が無いか探したところ、下記の拡張機能を発見した。

github.com

VSCodeでこの名前で拡張機能を探せばヒットする。かなり長い名前なので、以下でもこのsimply-view-image-for-python-debuggingのことを「拡張機能」と呼ぶ。


スポンサードリンク

拡張機能の使用方法

公式リポジトリのgifアニメをそのまま引用するが、これを見れば使い方は一目瞭然である。

  1. 実行したいPythonスクリプトを用意する。
  2. ブレークポイントを張る。(当たり前かもしれないが)ブレークポイントの位置は、画像を可視化したいオブジェクトを変数に格納した以降の行に張ることに注意。
  3. VSCodeでデバックモードを実行し、処理がブレークポイントまで達したら、
    1. 画像のオブジェクトが格納されている変数をクリックすると、もしもその変数が拡張機能で可視化をサポートしているオブジェクトならば、電球マークが現れる。
    2. 電球マークをクリックすると、View Imageというポップアップが出る。
    3. ポップアップをクリックすると、別ウィンドウで可視化された結果が表示される。

拡張機能内部で行われている処理はとてもシンプルで、 /tmp 領域にsvifpd2いうディレクトリを作り、画像をそこに書き出して、その画像を別ウインドウで表示するというものである。

各ライブラリのオブジェクトの可視化結果結果

機械学習の画像系タスクで主に使用されるであろうPythonライブラリのオブジェクトを可視化した結果をスクショ付きで述べる。

各ライブラリごとの可視化結果を下表に示す。中には画像処理用途とは限らないものも含めてしまっているが、実用途を鑑みての判断であるのでご了承いただきたい。

ライブラリ名 可視化可能か 検証した際のバージョン
Pillow 7.1.2
NumPy(, Scipy) 1.19.5
opencv-python 4.1.1.26
imageio 2.4.1
Matplotlib 3.4.2
PyTorch 1.6.0(torchvisionは0.7.0)
TensorFlow 2.7.0

なお、その他の動作環境のバージョンは下記の通り。

Pillow・・・○

readmeにはサポートしているとは書いていないが、可視化可能。

NumPy(, Scipy)・・・○

画像ではなく単なる数値のテンソルに対して可視化をおこなってみたが、可能だった。 また、ScipyもNumPyを内部で利用しているので可視化可能。

opencv-python・・・△

NumPyがサポートされているので、もちろんopencv-pythonの可視化も可能。

ただし、opencv-pythonではChannelの順序がBGRなので、残念ながらきれいに表示されないところが玉にキズ。

imageio・・・○

私は使わないが、imageioを試してみた所、可視化可能だった。

Matplotlib・・・○

画像を扱うためのライブラリでは無いが、先程張ったgifアニメの通り、MatplotlibのFigure、Axisの可視化も可能。

PyTorch・・・○

readmeにはPyTorchのテンソルもサポートしていると書かれていたので試してみたが、確かに可視化された。PyTorch民としてはかなり嬉しい。

また、驚いたことにBatch次元を考慮して表示してくれている。

TensorFlow・・・✕

readmeにはサポートしているとは書いていないが念の為可視化を試みた。
しかし、電球マークが現れなかったので対応していない模様。なお、私はTensorFlowはほぼ使用経験がないので画像のテンソルを作る方法が正しいかは自信がない。

おわりに

デバッグ時に様々なライブラリのオブジェクトに対して画像の可視化を試みた。
その結果、おおよそのライブラリで可視化可能ということがわかった。
Python開発の生産性の向上が期待できそうである。

なお、今回テストに使用したコードと画像は↓のリポジトリに置いているので、興味を持たれた方は追試に使っていただければ幸いである。 github.com


スポンサードリンク


  1. なお、「Jupyter Notebookならば楽に画像可視化できる」という声があるかもしれないが、私は開発にJupyter Notebookは用いない。その理由はこちらの記事で書いている。

  2. 拡張機能の頭文字をとったものと思われる

Open xINT CTF 関連ページ 非公式まとめ

AV TokyoでTeam pinjaによって開催されるCTF 『Open xINT CTF』関連の情報をまとめています。
ファンサイトの位置づけです。

目次

お知らせ(2022/01/16更新)

『その他のOpen xINT CTF関係のサイト』の項目を追加しました。

Open xINT CTF 2021のサーバはしばらくは残すそうなので、↓のアドレスから引き続き問題を閲覧・解答することができます。

公式情報

私の報告資料

有志の方々による参加報告資料

その他のOpen xINT CTF関係のサイト

Speaker DeckのPageViewをロギング&可視化する仕組みをコストを抑えて作った

f:id:meow_memow:20210915194634j:plain

この記事では、Speaker Deck上にアップロードしたスライドのページビュー(以下PV)を1日毎に収集し、日付ごとのPVをグラフとして可視化する仕組みをPythonで開発したことについて説明する。

目次


スポンサードリンク

背景

私が勉強会の発表スライドを共有する時、サービスはSpeaker Deckを使っている1

meow (@meow_noisy) on Speaker Deck

Speaker Deckを使っていて不便な点はいくつかあるが、その中の一つに1日毎のPVが記録されていない点がある。これがないために、どういうタイミングで何のスライドがどれだけ閲覧されているのかを分析できない。

したがって、1日でPVがどれだけ溜まったかを可視化するためにSpeaker Deck用のPVログ収集 & 分析する仕組みをコストを抑えて構築することにした。

アーキテクチャ

speakerdeckからPVを取得して可視化するまでの仕組みの全体像を先に示す。

f:id:meow_memow:20210915195039j:plain

区分けとしては下記の部分に分けられる

  • Speaker DeckのページからPVを取得しファイルに保存する部分
  • スクレイピングAWS Lambdaで行い、S3へ格納する部分
  • PVを記録したファイルからレコード単位に変換しDBに格納する部分
  • DBからレコードを取得し、PVをグラフとして可視化する

実装したコードは下記のリポジトリに配置している。

github.com

以下ではそれぞれの技術要素を説明する。

PVのスクレイピング部分

f:id:meow_memow:20210915195820j:plain

Speaker Deckにはスライドに関するメトリクスを取れるようなWebAPIもなければメトリクスのファイルダウンロード機能も提供されていない。したがって、ユーザがアップロードしたスライドページのスクレイピングを行い各スライドのPVを収集した。

ローカルでスクレイピングを実行するコードはリポジトリscrape_speakerdeck_pv.py に該当する。PVを取得したいSpeaker DeckのURLをこのスクリプトに書いた上で、$ python scrape_speakerdeck_pv.py を実行すると、result ディレクトリを作成し、その中にスクレイピング結果のtsvファイルを書き込む。

取得対象は、スライドのユニークID(Speaker Deck側が勝手に付与), スライドのタイトル名、PVである。

これを、次のようなフォーマットでtsvファイルで保存する。ファイル名は取得した日付にしている。

たとえば、2021-08-05.tsvは下記のような内容である。

ad7b2a026bd74e00a2c3915043c7eecb  Security Days Spring 2021のOSINTに関する講演4つの簡易まとめ/os_int_webinars2021spr    438
b9cb90f9e11845c9afe619a61c64798b    初心者向けOSINT CTF! Cyber Detective CTFに挑んだ!/cyber_detective_ctf  291
891f7fb535e14a54a85ee8792f734063    画面共有時にセンシティブな情報を意図せず公開してしまう事例の調査/sharing_screen_and_confidential_data   195
...

もちろん過去のPVは取得できないので、スクレイピングを開始した日からのロギング開始となることに注意されたい。

FaaSによるスクレイピングの定期実行

上記のスクレイピングをかつては手動のトリガーでやっていたがいい加減面倒だったので自動化した。

毎日1回定期実行といえばcrontabが第一に考えられるが、我が家には常時起動しているマシンはないためランニングコストを考慮してFaaSであるAWS Lambdaを使った。tsvのファイルの格納先はS3としている。

まず、LambdaにはS3を参照できるように権限を付与する。

次に実行するPythonの関数を登録する。Lambdaで実行する関数を含むスクリプトリポジトリlambda.py に該当する。これをコピーしてLambdaの関数として登録する。

スクレイピングに使用するrequestsBeautifulSoupはLambdaのPythonランタイムにはインストールされていないので、レイヤーを作成する。レイヤーの作成の仕方はAWS公式サイトの手順を参考にした。レイヤー作成時に気をつける点としては、python/lib/python3.8/site-package という構造でパッケージを格納する必要があることである。

この関数を1日1回定期実行するように設定する。すると、S3の指定バケットにtsvが格納される。

あとは、そのバケット$ aws s3 sync でローカルと同期すればよい。

Lambdaのエラー通知

Lambdaでエラーが起きた時にSlackに通知が飛ぶようにした。アラートの流れとしては、Lambda→CloudWatchAlarm→SNS Topic→AWS Chatbot→Slackである。これは下記のブログ記事を参考にさせていただき実装した。記事の通りSlackへのアラートをとても簡単に構築できた。

masakimisawa.com

tsvファイルをレコード単位に整形しDBへ挿入

tsvは日付ごとの各スライドのPVが記載されているが、これを各スライドごとの日付ごとのPVに変換してDBに格納する。

これらの作業はAWS GlueとRDSを使えば完全にAWS上で運用することができるが、ランニングコストがばかにならないのでローカルの方にDBを構える。

DBには手軽さを考えてSQLiteを使用した。

DBのER図は下図の通りである。slideテーブルでスライドを、slide_pb_dateテーブルでその日のスライドのPVを保持する。

f:id:meow_memow:20210915200740p:plain

このスキーマに沿うようにtsvファイルの各レコードを整形し、DBに挿入する。

この処理はリポジトリinsert_records_into_sqliteDB.py に該当する。

$ python insert_records_into_sqliteDB.py を実行すると、DBにレコードを挿入してくれる。

PlotlyでPVを可視化

この時点で、DBにはスライドに対して日付ごとのレコードが入っている。これをグラフでプロットする。インタラクティブにグラフを操作したかったため、Jupyter Notebookを立ち上げて、Plotlyで可視化する。

これはリポジトリvisualize.ipynb をJupyter Notebookを立ち上げればよい。

あとは、最後までコードを実行すると、下図のように各スライドの日付ごとのPVが可視化される。また、UIを操作すれば、スライドごとのPVを比較することもできる。

f:id:meow_memow:20210915201827p:plain

おわりに

Speaker DeckのPVをロギングし、可視化する仕組みをなるべくコストを抑えて実現する方法について説明した。
自分で溜まったログを可視化した感想としては非常に役立っている。急上昇しているスライドがあった時、何が原因でそうなったのかを調べるのが楽しい。また、今日はPVが増えていないと思っていてもグラフで見ると1つ増えているのがわかったりする。
このようにグラフを確認することで、次の発表へのモチベーションにしている。


スポンサードリンク


  1. Slide ShareではなくSpeaker Deckを使っている理由はこちらの記事で述べている。

Vue.js+Vuetifyをゼロから学習して簡単なブラウザゲームを作った

f:id:meow_memow:20210820213718j:plain

目次

Vue.jsを勉強し、数当てゲームを実装しWeb上に公開した。その開発過程を記す。

なお、開発物『16進数5桁版のhit and blow』は下記で遊べる。
『16進数5桁版のhit and blow』へのリンク


スポンサードリンク

はじめに

背景

「エンジニアたるもの専門以外の技術も身につけていくべきである。」という先人達の教えに従い、Python以外のことも学ぼうと思った。 何を勉強しようか考えた所、JavaScript(以下JS)は知っていて損はないだろうと思った。何かGUIのツールを他人に使ってもらうならセットアップは不要であったほうがいい1

私は勉強時、書籍を読んだだけだと経験上すぐ忘れるので、動くアプリを作ってリリースすることを学習目標として取り組んだ。

この記事はリリースまでの開発記の位置づけである。

私のWebアプリのフロントエンド開発経験

まず、スタートラインとなる私の開発経験は下記の通りである。

ここで言いたいのは、私はJavaScriptをまともに書いたことがないということである。

開発過程

プロジェクトスタート段階

この段階で決めたことは以下の通り。かなりいい加減である。とはいえ完璧さを求め出すと何も作れなくなるので、計画の大雑把さは意図的に心がけた。

  • 何を作るかの決定
    • 満たしたい条件
      • 最小構成で動くものがよい
      • いままでにないものがよい
    • hit and blowという数当てゲームのルールを改造したものを開発することにした。
      • 選定理由は、実装すべき機能が少なく、実際に遊んでみたかったためである。
  • どこまで作るかの決定
    • 小綺麗な見た目のUIまでは作りたいと思った。
  • 開発技術選定
    • Webフレームワーク
      • Vue.jsを主軸にWebページを作ることにした。
        • Vue.jsを選んだ理由は単純で、なんとなく流行ってそうだからである。
    • CSSフレームワーク
      • Vuetifyにした
        • Vue.jsのテンプレートと相性が良い
        • 日本語のドキュメントがあるのもありがたい。
      • ちなみに、始めた当初はVue.jsにリッチなUIが用意されていると思いこんでいたが、そうではなかった。
    • ホスティングサービス
      • Github Pagesとした
        • 理由は無料でデプロイも簡単なため。

開発期間(技術学習の期間も含む)

4月初旬から着手開始して7月末リリースなので、4ヶ月間程度かかっている。

  • 4月初旬~: Vue.js 自体の勉強
    • Vue.jsのチュートリアル
    • 他人が実装したサンプルを動かしながらVue.jsの挙動を確認
  • 6月初旬〜: Vue.jsを使わずに hit and blow の プロトタイプづくり
  • 6月下旬〜: Vue.js+Vuetifyで実装しリリース

各フェーズにやったことを以下で説明する

Vue.js 自体の勉強

Vue.js公式Webサイトにはチュートリアルが用意されていなかった。したがってガイドページの写経を行った。これを終えた段階では、スニペットのふるまいはわかるが、それが何が良くて何のためにあるのかわからないという状態である。

次に、Vue.jsのサンプル実装を写経することにした。サンプルはGoogleで「Vue サンプルページ」と検索してヒットしたもの↓を選んだ。
https://vuejs.org/v2/examples/index.html

写経をする中でtypoも発生するのでアプリが動かないということも起こった。その度にオリジナルのコードと違うところを目視diffをすることで解決した。

写経の後は、処理の流れを目視で追った。しかし、文法がわからないところがところどころ存在した。不明な箇所がVue.js起因なのか、JavaScript起因なのか分からなかったので、JavaScriptからやり直すことにした。

Vue.jsを使わずに hit and blow の プロトタイプづくり

JavaScriptの基礎の勉強にはドットインストールのはじめてのJavaScriptを見た。この時に写経はしていない。

その上で、hit and blowを1からHTML+JavaScriptで実装してみた。これはプロトタイプという位置づけである。実装したものは下記リポジトリに置いてある。

github.com

ちなみにプロトタイプもデプロイしているので下記リンクで遊べる。
プロトタイプ版『16進数5桁版のhit and blow』へのリンク
styleをほぼ指定していないため見た目がショボい。

実装にあたってはhit and blowのJavaScript実装例を探したが、ボタンで数字を入力するタイプは見つからなかったため参考にしていない。ひたすらコールバックを定義して、ボタンオブジェクトを取得して、clickイベントと対応付けることを繰り返した。

実装でこんがらがったのは、ボタンのdisableを制御することである。ゲームの状態(ゲームクリア、ゲームオーバーなど)に応じてこのステータスを更新する必要があるのだが、disableを開放するタイミングを網羅しなければならず、それをコードに落とすのが大変だった。

プロトタイプをVue.jsで書き直す

プロトタイプの実装を終えた時点で、JavaScriptの理解度は「調べればわかる」レベルまで到達したと感じたので、Vue.jsのサンプルコードを読むフェーズに戻り、Vue.jsのプロジェクトの構築の仕方を把握した。

また、辞書として『改訂2版 基礎から学ぶ Vue.js』を購入した。この本はオプション構成の説明が充実していてありがたかった。

改訂2版 基礎から学ぶVue.js [2.x対応! ]

改訂2版 基礎から学ぶVue.js [2.x対応! ]

  • 作者:mio
  • シーアンドアール研究所
Amazon

プロトタイプ開発の時点で必要な機能(正解の数の生成や、正誤判定など)は全部実装したので、後はVue.jsの機能を使えるようにコードを移植していった。

Vue.jsで書き直す中でデータバインディングは強力で感動した。プロトタイプだとボタンのdisableをイベントごとに書き換えていたが、Vue.jsならば算出プロパティでdisableの振る舞いを定義することでシンプルにボタンの状態を制御できた。

最終的にhit and blowをVue.jsで実装したプロジェクトリポジトリは下記である。

github.com

また、デプロイしたページへのリンクを再掲する。実際に遊べるので興味があればプレイいただければ幸いである。

meow-noisy.github.io

実装したかったけど、技術的に不可能だったこと

小綺麗なUIをつけられて満足しているものの、妥協した部分も勿論存在する。

  • 入力ヒストリーの初期位置がおかしい
    • 過去の入力を表示する表を右に配置しているが、なぜか最初はページの中央に配置されてしまいレイアウトのバランスがよくない印象をうける。
  • プルダウン形式のメモをつけられなかった
    • プレイ中に目星のついた桁をメモしておく場所を用意したかった。イメージとしては5桁分のプルダウンを並列に並べたかった。しかし、Vuetifyのプルダウンコンポネントであるv-selectを使用すると、1つずつ垂直に配置されてしまうのでやめた。しょうがなくテキストボックスで代替した。
  • チート防止
    • 結局、答えを格納しているオブジェクトをプレイヤーが参照できてしまう以上競技性がない。したがって、正誤判定サーバを用意して通信したかったが実装方法がわからなかったのでやめた。ここらへんはもう少しWeb開発力が上がってからチャレンジし直したい。

おわりに

開発のふりかえり

勉強のためにVue.jsでアプリを開発することにした。ロジックの単純なプロダクトをつくるという目標を達成する上で、hit and blowを題材にしたのは正しかったと思う。

プロトタイプはJSしか使わなかったこともあり、Vue.jsのありがたみを感じることができた。とはいえ、Vue.jsのコンポーネントやルーティングのような機能は使っていないので、「Vue.jsで実装できます」とは言えないであろう。今後も精進していきたい。

姉妹記事

自分で作ったゲームを遊んだ時の感想を別の記事で書いた。遊んでみてゲームデザインも大切だということがわかったのはいい経験であった。

meow-noisy.hatenablog.com


スポンサードリンク


  1. Pythonで動くGUIのツールをユーザが使おうとすると、Pythonやライブラリを自前でインストールしなければならず、導入負荷が高い。

connpassのイベント参加者のTwitter idを収集し、Twitterリストを作成するPythonスクリプトを書いた

f:id:meow_memow:20210725140704j:plain

目次

日本のエンジニア向けイベント管理サービスはconnpassがメジャーだと思います。
イベント中にコメントをもらいたい時はTwitterハッシュタグを用意して参加者につぶやいてもらう運用をよく見かけます。一方で、ハッシュタグをつけずにつぶやく方も一定数いらっしゃる印象です。
そこで、connpassの参加者リストから、Twitterを登録している方のTwitter idを集めてリストを作っておいたら、何か違う観点のコメントも拾えたりできるかもしれないと思いました。

参加者のリスト作成は人力でもできますが、面倒なことなのでPythonにやらせることにしました。この記事ではそのスクリプトの使用方法をご紹介します。
リポジトリは↓です。リポジトリをcloneしてモジュールをコマンドラインで実行する想定です。

github.com

なお、Twitterのリストを自動化する部分ではTwitter APIを使用しています。このAPIを使うにはTwitter社への申請が必要で、申請作業はAPIの用途を記述する必要があり、かなり大変だったことを先に述べておきます。


スポンサードリンク

スクリプトの説明

処理は2段階に分かれています。

  1. connpassのイベント参加者リストのスクレイピング
  2. Twitterのリストを作成

それぞれを以下で説明します。 スクリプト自体は処理ごとに独立しているので、どちらか片方の処理だけを行うこともできます。

connpassのイベント参加者リストのスクレイピング

この工程では、connpassの特定のイベントにおける参加者リストをスクレイピングします。なお、connpass APIを介さないスクレイピングはあくまで禁止されていないというだけであり、robots.txtとマナーを遵守する必要があります。

スクリプトは下記です。スクレイピング処理が中心なので、見ても面白くないかもしれません。
https://github.com/meow-noisy/create_connpass_participants_twitter_list/blob/master/get_participants_id_from_connpass.py

使い方は
$ python get_participants_id_from_connpass.py <connpassのイベントURL> [オプション]です。

下記はpython get_participants_id_from_connpass.py -hのヘルプの説明文です。

usage: get_participants_id_from_connpass.py [-h] [-o OUTPUT_TXT]
                                            [-s {twitter,connpass}]
                                            [--exclude_cancel]
                                            url

positional arguments:
  url                   connpassのイベントURL

optional arguments:
  -h, --help            show this help message and exit
  -o OUTPUT_TXT, 
                        出力ファイルへのパス
  -s {twitter,connpass}, --service {twitter,connpass}
                        どのサービスのアカウントをリストとして取得するか
  --exclude_cancel      このオプションを追加すると、キャンセルしたユーザをリストに含めない

補足すると、

  • --output_txt OUTPUT_TXT
    • 出力ファイルパスはオプションにしています。もしも与えない場合は、urlを加工した名前のファイルがモジュールを実行された場所に生成されます。
  • --service {twitter,connpass}
    • 参加者リストから収集するidをtwitterconnpassかを選びます。デフォルトではtwitterです。
  • --exclude_cancel
    • あくまで参加した人だけでリストを作りたい時のことを想定してフラグをつけました。

これを実行すると、運営、参加者、キャンセルしたユーザのidを取得し、idが1行ずつ書かれたtxtファイルが生成されます スクリプトで工夫した点としては、参加者が100名を超えていると別ページが作られるため別ページを巡回してスクレイピングしている点です1。 ただし、運営もしくはキャンセルが100人を超えている場合に作られる別ページは見に行きません。運営orキャンセル人数が100人を超える例を見たことがないのでサポート外にしています。

Twitterのリストを作成

(プライベートな)Twitterリストを作成します。それにあたっては前述の通り、Twitter APIの申請が承認されている必要があります。また、Twitter APIトークン発行時にはWrite権限を付与しておく必要があることに注意してください。
リスト作成にあたってはTweepyというサードパーティPythonラッパーを使用しました2$ pip install tweepyでインストールできます。

スクリプトは下記です。このスクリプト中にトークン情報を設定する箇所があるので、こちらを書き換えてください。 https://github.com/meow-noisy/create_connpass_participants_twitter_list/blob/master/create_participants_twitter_list.py

使い方は
$ python create_participants_twitter_list.py <ファイルパス> [リスト名]

第1引数はTwitter idが1行ずつ記載されたファイルです。また第2引数はリスト名です。第2引数が設定されていない場合はファイル名がリスト名になります。 実行すると、APIと紐づくアカウントにTwitterリストが作られます。

まとめ

connpassイベント参加者のTwitterリストを自動で作成するスクリプトを開発したので紹介しました。
今後の課題としては、リストを作った後、イベントに関するツイートだけを自然言語処理で抽出するようなスクリプトを実装してみたいです。


スポンサードリンク


  1. このスクリプトを作ろうとした動機として、別ページの取得がサポートされているconnpassのスクレイピングスクリプトがなかったということもあります。

  2. WebAPIのURLをPythonのメソッドに置き換えれば操作できることが多いので直感的で便利です。