建設予定地

当面はやったことの備忘録

Open Hack U 2020 Vol.4に参加して

はじめに

開催からだいぶ間があいてしまいましたが、先日参加したヤフー主催のハッカソンOpen Hack U 2020 Vol.4の振り返りを行いたいと思います。
今回、惜しくも入賞は叶いませんでしたが、1つのプロダクト開発のために駆け抜けた2週間は楽しかった。

Open Hack Uについて

hacku.yahoo.co.jp

  • ヤフー社員の方のサポートのもと、学生が2週間でプロダクトを企画・開発・発表するイベントです。
  • 今回はチームメンバーの対面での開発禁止という完全オンラインの形式で開催されました。

本番のプレゼン動画
www.youtube.com

作ったもの

自分達のチーム「焼肉焼いたら家焼くな」では、「漫才」×「言語処理」をテーマに、あるキーワード(ボケ)に対して韻を踏んだ太めなキーワード(ツッコミ)を返してくれる漫才LINEBotを作りました。
元ネタはタイムマシーン〇号な某芸人さんです。

デモ動画はこちら
youtu.be

GitHubソースコードはこちら
github.com

開発期間

最初の1週間は事前学習にあてていて、実際の開発期間は1週間でした。

仕様技術

  • LINE Messaging API
  • Python
  • Heroku(本番運用)
  • ngrok(開発用サーバー)

アルゴリズム

大まかなロジックは
入力された文字列をローマ字に置き換える

母音が一致する食べ物関連の単語を辞書(csvファイル)から引っ張ってくる

ここから最も太らせスコアの高いものを出す

というものでした。

詳しい実装の流れとして、
ユーザーにより入力されたワードのローマ字変換をライブラリ(pykakasi)で行う。
同様に単語辞書のキーワードもローマ字への変換を行い、さらに母音に変換する前の単語をリストのインデックスから分かるように辞書を作成しておく(pandas)。
ユーザーワードと、食べ物関連の単語辞書からそれぞれ母音を抽出したものを引数としてランキングを生成する関数に渡す。
ここでスコアを生成する関数を呼び、渡されたワードの文字列がリストのものよりも長い/短い場合、短すぎる/長すぎる文字列にマイナス報酬を与える処理を行う。
上記の処理の結果より、母音変換前のリストのインデックスとスコアをセットで取得し、ランキングをfor文で回してトップスコアの単語を母音分解前のものに戻し、日本語に復元してメインクラスに返す。

このアルゴリズムには、母体となるデータ(単語辞書の語彙)が不足していることから、返しが特定のワードに偏ってしまう、思うように韻が踏めているとは言い難い(5文字の入力ワードに対して2文字の韻踏みワードがかえってきてしまうなど)という問題点がありました。

より精度を上げるため強化学習周りの調整として、

  • 辞書の語彙数を増強

  • ラップの韻踏みで重要視される末尾の得点変化率を強くする

などの調整を行いました。
精度の向上については、文字数ごとの単語辞書を作ったり、スコアリングのチューニングをユーザーにゆだねた双方向性的なシステムにする、というような調整も可能そうです。

当初はライブラリ(Word2Vec)で単語をベクトル化し、太らせスコアを出す予定でしたが、 Word2Vecの引数にローマ字をわたせず、スコア化にあたって上記のような自前のアルゴリズムを実装した、というような経緯がありました。

また、今回は実装が間に合いませんでしたが、マルコフ連鎖を用いて文章レベルでの応答を実現させるという展望もありました。

f:id:denham:20210326233915p:plain 秀逸な返しの例

開発方法

基本はDiscordで集まって夜通し作業や進捗共有をし、開発はソースコードをColaboratoryで共有しあいながら行いました。
LINEBotでのフロント処理を行う部分と、バックのpythonアルゴリズムをクラスで切り分けてしまえば、
機械学習ライブラリの試しや細かい調整などは共通プラットフォームであるColaboratoryで行うのが一番だったためです。

僕は今回、ライブラリ調査やスコア化アルゴリズムの実装、デプロイ環境周りの調整を行いました。
また、本番環境にAWS(Lambda)を導入したかったのですが、こちらは実装が間に合わず頓挫。
というのも、pykakasiのような外部モジュール扱いになるpythonライブラリを、Lambdaの実行環境(レイヤー)に導入することが出来なかったためです。 pythonのバージョンを下げて再構築など行ったもののうまくいかず...。

Lambda パッケージ用にDocker イメージを使用する などの方法を用いればうまくいきそうな気がしています。
要研究ですね。

ログ監視の所感

デプロイ回りは最終的にはHeroku、開発時と発表時はngrokで行っていました。
f:id:denham:20210326232841p:plain 発表時のサーバー稼働中はPCが激重案件。もっともchoromeのタブをゆうに50件は開いていたため、実にブラックな労働環境を強いていました。ごめんね。

とはいえログを眺めていると気が付くことはあって、POST過多による処理落ちかなぁと思っていた部分が、ランキングリストのデータが空のときのエラー落ちの可能性が高そうだ、と見ています。
ということは、ここは精度調整と絡めれば改善の見通しが立てられそう

最後に

めちゃくちゃ面白いアイディアの企画・立案、完璧なチームマネジメントとスライドの作成兼発表をやってくれたS君(段取り完璧すぎた。彼の声掛けがないと自分はハッカソン自体参加してなかったです)、
アイディア実装の核となる韻踏みアルゴリズムの発案と機械学習周りの実装、最後まで絶妙なスコアリング調整をやってくれたO君(最終日の朝6時半までともにAWSと格闘してくれて感謝!)
LINEBotのナイスな掛け合いの実装やリッチメニューなど画面デザイン、ピンポイントでGitHubソースコードを共有してくれたNさん(最終日激アツでした、ありがとう!)、

2週間お疲れさまでした。
次はジョ〇マンBotやりましょう!

(奇しくもインターン先でAWS機械学習を触らせてもらえることになったので、AWS力をなるたけたくわえておきます。)

LINEの友達登録はこちらから。
f:id:denham:20210326235530p:plain
ぜひ遊んでみてください!

npmの権限関係エラーの対処法

現象

Error No.127
npm ERR! code 127
npm ERR! path /home/hogehoge
npm ERR! command failed
npm ERR! command sh -c create-next-app "fugafuga" "https://github.com/~~~~~"

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2021-03-23T02_49_40_026Z-debug.log

と遭遇した

環境

ubuntu18.04(WSL)
npmバージョン/7.6.3

上記は$PATH関係か、タイポなどで起こるエラーっぽい。詳しくは後述。
npmの公式サイトでは、-gオプションでグローバルインストールしたときなどにpermission deniedされた場合の対処法として、以下が推奨されているようです。

対処法

npm config get prefix

でnpmディレクトリのパスの所在を確認

ここでやることが変わる。

ケース1

"/usr/local"

にあったときは、

sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}

npmディレクトリのオーナーを自分のアカウント(whoami)に変更する

でOK

ケース2

"/usr"  

にあったときは、 docs.npmjs.com を参考に、

1.npmのグローバルインストール用のディレクトリを作成

mkdir ~/.npm-global

2.上記で作成したディレクトリをprefixのパスにセットする(npmの権限を~/.npm-globalに変更)

npm config set prefix '~/.npm-global'

3.パスを通す

export PATH=~/.npm-global/bin:$PATH  

(あるいはvi .bashrcで直接記述する)

source ~/.profile

でOK

やらかしたこと

①前日の夜中にタイポを権限関係のエラーと誤認して、パスを/.npm-globalに通したまま寝る→
②npmのパスが作業ディレクトリ(/usr/local/)にないので弾かれる(sudoでいけると思ったけど駄目でした)→
そこからなんやかんやあってタイポでパスを飛ばすなどやらかし、結局

  • export PATH=~/usr/local/bin:$PATHして、
  • npm config set prefix '/usr/local'でパスを通す
  • n packageでnodejs, npmを入れなおす
  • ケース1に戻る

という工程を踏んで復旧させました。

Error No.127は②で起こっていたエラーで、/home/hogehogeにパスないよ、と怒られていたわけです。(回り道をしてしまっていた)
寝ぼけ頭で作業するとあかんですね。気を付けます...

参考
npmでpermission deniedになった時の対処法[mac] - Qiita
Resolving EACCES permissions errors when installing packages globally | npm Docs
【備忘録】Node.jsでパーミッションで怒られが発生してグローバルインスコができなくなったときに読むもの | Tech_Kazu.log

Vuetifyを含んだコンポーネントのテスト実行時に出る警告を消す

現象

Jestテスト実行時に出る

「jest [Vue warn]: Unknown custom element: <v-btn> - did you register the component correctly? For recursive components, make sure to provide the "name" option.」  

などの赤字の警告について

テストツール

Vue test Utils, Jest

原因

Vuetifyコンポーネント(<v-col>や <v-btn> )をJestが認識しないため、Unknown custom elementエラーが出る

対処法

package.jsonのscriptsに以下を記述

package.json
 "scripts": {
+    "test": "jest --config jest.config.js"
  }

次に、jest.config.jsで指定した階層(root直下のtestディレクトリ)に以下のファイルを追加

test/jest.setup.js  
import Vue from 'vue'  
import Vuetify from 'vuetify'  
  
Vue.use(Vuetify)  
  

以下のファイルをroot直下に追加

jest.config.js  
module.exports = {  
  setupFiles: ['<rootDir>/test/jest.setup.js'],    // Load Mock setting  
  moduleNameMapper: {  
    '^@/(.*)$': '<rootDir>/$1'  ,
    '^~/(.*)$': '<rootDir>/$1'  
  },    
  transform: {  
    '^.+\\.js$': 'babel-jest',  
    '.*\\.(vue)$': 'vue-jest'  
  },  
  moduleFileExtensions: ['js', 'json', 'vue']  
}  

Vuetifyのようなライブラリを含んだコンポーネントに対するテストは、テストランナーに応じた特別な処置が必要になります。
クリックテストなどでも、Vuetifyのボタンコンポーネントに対するテストを書くにはプラグイン登録が必要になります。

このあたりを参照したいときは、Vuetify公式チュートリアルユニットテストの手法が参考になりそうです。
vuetifyjs.com

参考

vuetifyjs.com qiita.com

【解決】画像ファイルの読み込みでシンタックスエラーが発生する

画像ファイルをインポートする必要のあるVueコンポーネントをjestでテストした際に発生したエラーでしたが、大分長いこと格闘して、ようやく解決したのでまとめておきます。

症状

Vue.jsのテストコードを書いていて、以下のようなシンタックスエラーに遭遇した

/home/user/hogehoge/src/assets/image.png:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){�PNG
                                                                                         

SyntaxError: Invalid or unexpected token
テストツール

Vue test Utils, Jest

テスト対象のコンポーネント
// sampleComponent.vue
<template>
    ...
</teplate>
<script>
  name: 'sampleComponent',
  mixins: [sampleMixin],

  data() {
    return {
      hogehoge: require('@/assets/piyopiyo.png'),
    }
  },
  methods: {
    move() {
      ...
    },
  },
}
</script>
<style></style>

原因と対処法

data()メソッドの返り値を画像ファイルとした場合のみに発生するエラーで、つまるところ

画像ファイルをインポートすると、Jestは画像のバイナリコードを.jsとして解釈しようとするため、エラーが発生します。
reactjs - 画像をインポートするとjestテストが壊れます - ITツールウェブ

ということらしい。インポート時画像ファイルを正しく解釈できるようにする必要があります。

なのd、package.jsonのtransformとmoduleNameMapperに以下のような設定を追加してあげましょう。
jestjs.io

// package.json
    "transform": {
      ".*\\.(vue)$": "<rootDir>/node_modules/vue-jest",
      "^.+\\.js$": "<rootDir>/node_modules/babel-jest",
+      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/fileTransformer.js" 
    },
    "moduleNameMapper": {
+      "\\.(css|scss)$": "<rootDir>/__mocks__/styleMock.js", 
+      "^@/(.*)$": "<rootDir>/src/$1" 
    }

transformでは画像ファイルの拡張子がインポートされたときにroot直下にあるfileTransformer.jsをインポートするように指示しています。
同様に、moduleNameMapperでは、css、scssがインポートされたときにrootディレクトリからmocks/styleMock.jsを参照するように指示しています。
なので、package.jsonの追加記述と対応させてrootディレクトリ(基本的にはpackage.jsonと同階層です)*1にfileTransformer.js、mocksディレクトリを作成し、mocksディレクトリにはstyleMock.jsを作成します。
そのうえで、これらには以下のように記述します。

// fileTransformer.js
const path = require('path')

module.exports = {
  process(src, filename, config, options) {
    return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'
  },
}
// styleMock.js
module.exports = {}

以上の設定のもと、テスト対象のコンポーネントをmount/shallowMountして検証することにより、問題なくテストが通りました。

参考
【Jest】画像ファイルやCSSのインポートに失敗する場合の対処法 - Qiita
reactjs — JestとReactおよびCSSファイルのインポートを伴うSyntaxError

明けたけど、2020年を振り返る

あけましておめでとうございます!
あけましたが、せっかくなのでざっくりと2020年を振り返っていきたいと思います

終わってみるとあっという間でしたねー。
対面コミュニケーションでの制約があった分、オンラインを通じてたくさんの出会いがあり、多方面で色々な方にお世話になりました。
アウトプットを意識するようになったのも大きな変化で、学びの多い一年でした。

1月

Local学生部に加入して最初の総大会に参加。
第11回 LOCAL学生部総大会 開催レポート

前年に参加したMH4YがWordPress脆弱性を題材としていたため、Webをやりたい!という気持ちがありました。

2月

ドットインストールとProgateとpaizaを右往左往してやっていた記憶。
gitで初コミットしたり、glideでアプリを作ったり、PHP本を一冊やる。


4月

CSSの勉強をちゃんとする(決意をする)。

cistLTに参加したのもこのころでした。
cist-lt-group.web.app

5月

この辺りからUnityの勉強をちゃんと始める(Udemyの講座を買う)
Javaの勉強をはじめる

6月、7月

2回目のOSC(オンライン)に参加する。
キャリアセレクトさんのDjango勉強会に参加したり、
技育祭に参加したり。
talent.supporterz.jp


ポチる。

8月

ひっそりはてブ開設

SC4Yに参加する
hackteck.hatenablog.com

大学のプロジェクトの成果物としてGAS×Glideのアプリをつくる。

Unityの3D系チュートリアルやったり

Unity1Weekに参加し、初めて自作ゲームを公開する。
unityroom.com

最初のポートフォリオを作る。(コンテンツはほぼなし)

9月

初LT
cist-lt.connpass.com
cistLTのポートフォリオハッカソン2個目のポートフォリオを作る。
Firebaseに足を向けて寝られなくなる。


Laravelとかやってた(フレームワークに初めて触れた)。

10月

プロメン成果物のアプリをWeb版で公開。
chitose-fts.web.app
フロントエンドに強くなりたいという気持ちになる。


11月

セキュリティミニキャンプに初参戦。


WSL2でGUIを使ってみたり
GASでユーザー登録フォームを作った
Reactの素振りをしたり


ポートフォリオ(3個目)を作った

12月

momijiLTで初めてチーム開発を経験。
unityroom.com

久しぶりに触れたUnityが楽しすぎて、ここにきてUnity熱が再熱。

小学生向けプログラミング教室に参加したり、
Springbootの勉強をしたり、大学の課題でLineBotを作ったり。

12月からは長期インターンに参加させていただけることになり、Vue.jsのテストを書いたりしています。

抱負とか

今年はゲーム作りやCTFやハッカソンなど、去年やり残したことをやっていきたいですねー。
f:id:denham:20210101013547p:plain
もっと草を生やし隊。

Open Hack頑張る!

Vuetifyでちょっとリッチな画面を作る

こんにちは!この記事はLOCAL学生部アドベントカレンダー22日目への寄稿です。

adventar.org

今回はVuetifyのUIコンポーネントはいいですね、というお話です。
vuetifyjs.com

Vue.js+Vuetifyの素振りとして自作のポートフォリオを作り直してみたのですが、ほぼCSSなしでいい感じにレイアウトを組めてとても良いですね。
実装されているコンポーネントの種類が豊富さが凄い。カレンダーやグリッドデザイン、カルーセルからタイムライン表示までetc.

基本的にはメインのコンポーネントに固有コンテンツを記述し、メニューバーはv-navigation-drawer, ヘッダーはv-app-bar,フッターはv-footerといったようにパーツ単位でUIコンポーネントを選択してレイアウトを組むことになりますが、プラモデルを組んでいる感覚に近いです。

ワイヤーフレームの例を見ていると、Discordのクローンなんかも作れそうですね。

余談ですがUIについて
mkan-0141.hatenablog.com このようなエントリーを見かけて、次はFlutterを使って何か作ってみようかなぁと考えています。

ちとせオンラインワークショップに参加した話

こんにちは。この記事はcistLTサークルアドベントカレンダー18日目への寄稿です!
adventar.org

技術系とは少し毛色が異なりますが、今回は11/28(土)に行われた、ちとせ創業支援オンラインセミナーのWSに参加した話をしたいと思います。

f:id:denham:20201211211853p:plain

本来は2部構成のWSのようですが、初回のほうは都合上参加できず、2回目となる28日に参加させていただいていました。

講師はPLAYWORKS代表のタキザワケイタさん。

プログラム前半で3分アイデアソンを行い、後半ではチーム分けを行い、ビジネスモデルキャンバスとストーリーボートの作成を通じて発想した事業を具体化する、という構成でした。

イデア発想法

「アイデアとは、既存の要素の新しい組合せ以外の何物でもない」
『アイデアのつくり方』J.ヤング(1988)

ヤング先生いわく、アイデアは既存のアイデアのかけあわせから生まれる。ということで、アイデアソンのテーマはかけあわせです。

f:id:denham:20201211220416p:plain

かけあわせとは?
お題となるテーマ×テーマから遠いモノをかけあわせ、強制発想(リフレーミングし、新しい価値を見つけ出すという考え方

イデアソンとは?
イデア×ラソンを意味する造語で、特定テーマについて短時間に集中してアイデアを出し合うイベント

今回のアイデアソンでは、3分間隔で新しい掛け合わせテーマが提示されるので、それら2つのテーマにまつわる要素をかけあわせた事業アイデアを出し続ける、ということを行いました。

たとえば
f:id:denham:20201211213337p:plain こちらは「和食」から連想される要素をかきだしてみて、プリンとの掛け合わせを考える、という例ですね。

f:id:denham:20201211214518p:plain
こちらはバスプリン。語感がよいです

出題テーマは千歳市×あつ森、Uber Eats、ペヤング、グランピングetc。

3分アイデアソン、実際やってみると想像以上にしんどいです笑
チームの皆さんの発想力豊かさに唸らされっぱなしでした。
柔軟な発想力ほしいです。頭の体操大事。

後半はチームに分かれて、アイデアソンであがった一つのテーマについてのブラッシュアップを行いました。

こうしたアイデア出し→具体化の手法は、起業にかかわらず、個人開発など色々な場面で活かしていけるなぁと思いました。