soy-curd's blog

へぼプログラマーです [https://twitter.com/soycurd1]

机の上においたボタンを押すとリアルタイムでSlack上に寿司を食べたいことを通知できるシステムを構築した

机の上においたボタンを押すとSlack上に寿司を食べたいことを通知できるシステムを構築した。

github.com

動機

寿司を食べたい時にそれをSlackで報告するためわざわざキーボードに手を伸ばすのは非常に面倒である。机の上に専用のスイッチがありそれを押すだけで寿司が食べたいことを通知できれば非常に素晴らしいユーザー体験が得られるはずだ。昔遊んでいたArduinoが家にあったので、それとSlackを組み合わせることでシステムを構築していく。

作り方

Arduino

100均に行ってボタンっぽいものを購入する。

f:id:soy-curd:20160619234139j:plain

スイッチ式のシーリングライトがあったので流用。ライトなので押すと光る。

f:id:soy-curd:20160619234206j:plain

(光る)

気合で内部のスイッチを見つけ出して配線。

f:id:soy-curd:20160619234400j:plain

内部を開けるとこんなかんじになっていた。緑と黄色のリード線は自分で結線した。

f:id:soy-curd:20160620000154j:plain

適当に抵抗を繋いでやばい電流がArduinoにいかないようにする。 回路図を示したいところだが、そもそも100均ボタン側の回路が不明なのでプレッドボード側も勘で配線した。 とりあえず5Vを直接グラウンドに繋いではいけないことがわかった。

Arduinoにコードをデプロイ。

const int SWITCH = 2;

void setup() { 
  pinMode(SWITCH, INPUT);
  Serial.begin(9600);
}

void loop() {
  if (digitalRead(SWITCH) == HIGH) {
    Serial.print("1\n");
  } else {
    Serial.print("0\n");
  }
  delay(500);
}

Arduinoに繋いでシリアルモニタでチェック。"1"と"0"がボタンに応じて出力されていればOK

。出てなかったら配線とか抵抗を見直す。

Python

PySerialというモジュールがあるので、それをpipでインストール(Arduinoとの通信)。

Pythonでシリアル通信 - Qiita

こちらを参考にした。

slackwebというモジュールがあるので、それをpipでインストール(Slackとの通信)。

SlackのIncoming WebHookを用いた。異常に簡単にSlackにメッセージを流せる。

Slackにincoming webhook経由でpythonからメッセージをPOSTする - Qiita

コードはこちらを参考にした。 Incoming WebHookのURLは環境変数に格納し、実行時に読み込むようにした。

pythonを実行

import slackweb
import os
import serial
import time

url = os.environ["SLACK_OHA"]
slack = slackweb.Slack(url=url)

port = "/dev/cu.usbmodem1421"
current_oha_state = False

# ボード側は500ms間隔でserialにHIGH -> b"1", LOW -> b"0"を送信する
with serial.Serial(port=port, baudrate=9600, timeout=1) as ser:
    while True:
        # time.sleep(0.1)
        flag = ser.readline()
        # switch ON
        if (bytes(b"0") in flag and not current_oha_state):
            current_oha_state = True
            print("oha")
            slack.notify(text="スシ食べたい🍣")

        # switch OFF
        elif (bytes(b"1") in flag and current_oha_state):
            current_oha_state = False
            print("otu")
            slack.notify(text="スシいらない🍣")

        print(flag, current_oha_state)

結果

とりあえずボタンをポチポチ押す。すると、

f:id:soy-curd:20160619231258p:plain

ほぼリアルタイムでこのようになる。ギリギリIoTの範疇に入る気がする。

これで快適に他人にスシを食べたいことを通知できるようになった。 会社のCTOがArduinoの無線モジュールを入手したようなので、それを使えばボタンをPCから独立させることも可能な気がする。未来っぽい。

最急降下法をPythonで実装した

関数x2 + xは、どうやら最小値がひとつだけ存在するらしい。どうしてもその値を知りたくなったので、最急降下法を使ってPythonでその値を求めてみた。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import random

learning_late = 0.01
loop_max = 1000

def main():
    
    initial = random.uniform(-10, 10)
    old = initial
    new = 0.0
    for _ in range(1000):
        
        # 更新式
        new = old - learning_late * diff_func(old)
        
        # 目的関数の値を確認
        y = object_func(new)
        print(y)
        
        # 値を更新
        old = new
        
    print(new, y)
        
    
# 目的関数
def object_func(x):
    return x * x + x
    
# x ^ 2 + x の微分
def diff_func(x):
    return 2 * x + 1

if  __name__=='__main__':
    main()

これを実行すると、

# x、y
(-0.5000000145792641, -0.24999999999999978)

と、xが-0.5付近の時に目的の関数が最小値となることがわかった。なるほどというかんじ。

ITエンジニアのための機械学習理論入門

ITエンジニアのための機械学習理論入門

言語処理のための機械学習入門 (自然言語処理シリーズ)

言語処理のための機械学習入門 (自然言語処理シリーズ)

新年になったのでGIF作成webアプリ「にこ☆GIF」をmp4に対応させた

正月休み中暇だったので、ニコニコ動画からGIFを作成するツール「にこ☆GIF」をmp4に対応させた。

にこGIF

どんなかんじか

例えばこんな動画をGIFに変換すると、 f:id:soy-curd:20160102150035g:plain

となる。

実装について

基本、ffmpegで動画ファイルをGIFに変換するだけなのだが、なにも考えずに行うと動画をいちいち全て取得する必要があって、異常に時間がかかっていた。

そこで、動画の途中からデータを部分的に取得するように実装したのだが、そのようにすると動画として再生できないバイナリの断片しか得られない。そこで、pythonを使って力技でファイルの断片から.264ファイルを擬似的に作成して、それをffmpegでmp4に変換することにした。mp4と.264、仕様書は存在するのだが、英語が全然読めなくて非常に辛かった。結局はmp4のバイナリを目で読んで仕様をバイト列から類推するみたいなかんじになった。一年分はバイナリエディタを使ったと思う。当分16進は見たくない。

参考にしたもの

mntone.hateblo.jp

stackoverflow.com

developer.apple.com

https://www.cmlab.csie.ntu.edu.tw/~cathyp/eBooks/14496_MPEG4/iso14496-10.pdf

太宰治小説のTF-IDFを計算した

http://takuti.me/note/tf-idf/

を参考に、太宰治小説のTF-IDFを計算した。以下、値が上位のものから単語だけ30個並べると、

いちど,謂わ,依っ,要ら,つらい,やたら,ご存じ,在っ,先日,おそろしく,だいいち,
あがっ,ゆるし,下さっ,おそろしい,落ちつき,青森,あいだ,くるしい,おそく,ちかく,
おまえ,ごはん,大笑い,具合い,判ら,ちまい,のこのこ,としの,三鷹,やけくそ,たいてい,
れい,だめ,無かっ,ちゃんと,逢う,津軽,あらわれ,繰りかえし,わびし,あれこれ,わかい,
うつむい,みじん,いやらしい,わがまま,ふるさと,於い,在る,口調,なんにも,捨て,
無学,ッ,なさっ,だいたい,概念,判る,長兄,生家,安易,素知らぬ,金木,したため,熱狂,
あいそ,のろのろ,ごちそう,わるい,芸術,ばん,之,落ちつい,まじめ,ことし,敗北,自重,
たのしみ,めいわく,長編,ロマンス,かたち,悪癖,言える,記さ,あからめ,知合い,滅茶,
とたんに,あやしい,心掛け,判っ,くさく,高潔,まごつい,侘び,あたらしく,甲府,ひくく,
むだ,瞬時,ふっと,酔っ,大  声,きらい,内心,おのれ,ゆえ,ふたり,おめでとう

のようになる。ネガティブな単語が多く含まれていて、非常に満足度が高い。

https://github.com/soy-curd/Dazai/blob/master/doc/flayer.png

ついでに文学フリマ用のビラもできたので、ひと安心。

小説風文章生成アプリ『Bungoo』を少し改造した

boonlab.hatenablog.com

今までは小説のデータを取ってくるのに毎回毎回青空文庫からデータを引っ張ってたのだけれど、 さすがにDosアタックしてるような気持ちになったので、PostgreSQLにデータを全部突っ込んでそこからデータ取るようにした。

ついでに、markov chainを作るのにめちゃくちゃ時間がかかっていたので、それもあらかじめ作成してpickle.dumpsしてデータベースに格納した。これで高速化できるはずだったけど、調子に乗って太宰治の小説を200本くらいポスグレにいれたせいで文字列を作るほうで時間かかってしまい差し引きゼロになった。むしろ遅くなったかもしれない。残念。

github.com

文學ラボに同人誌の原稿追加した

boonlab.hatenablog.com

前につくった、

http://bungoo.herokuapp.com/Bungoo/

の紹介記事のようなもの。

しかし、そろそろ校正しないと文学フリマに間に合わなそうでやばい。まじでやばい。

python用のシリアライザmarshmallowで辞書オブジェクトとJSONの相互変換

pythonのシリアライザであるmashmallowを業務で使うことになったのでメモ。

はじめに

webアプリケーションを作成していると、

{
    'hoge':'1',
    'foo':'2',
    'bar':3
}

のような辞書オブジェクトを、

'{"foo": "2", "hoge": "1", "bar": 3}'

のようなJSONに変換したい場合(やその逆)がある。その時に使えるのが、marshmallowである。

github.com

インストール

pip install marshmallow

でOK。

簡単な例

まず、marshmallowを使うには、スキーマというものを定義する必要がある。

たとえば上の例だったら、以下のように定義する。

# スキーマを定義
class HogeSchema(Schema):
    hoge = fields.Str(required=True)
    foo = fields.Str(required=True)
    bar = fields.Int(required=True)

これを使うと、

hoge_data = {
    'hoge':"1",
    'foo':'2',
    'bar':3,
}

data, err = HogeSchema().dumps(hoge_data)
pprint(data)
pprint(err)

# '{"foo": "2", "hoge": "1", "bar": 3}'
# {}

というふうにJSONに変換することができる。

逆の場合は、

hoge_data = '{"hoge":"1","foo":"2","bar":3}'

data, err = HogeSchema().loads(hoge_data)
pprint(data)
pprint(err)

# {'bar': 3, 'foo': '2', 'hoge': '1'}
# {}

として、loadsを用いるとJSONストリングを辞書オブジェクトに変換できる。

なお、marshmallowはバリデーション機能も持っているので、さきほどのスキーマを用いて、

hoge_data = '{"hoge":"1","foo":"2"}'

のようなフィールドの不足したデータをデシリアライズしようとすると、

# {'bar': ['Missing data for required field.']}

と、エラー理由を戻り値に格納してくれる。

応用

marshmallowであるが、フィールドを拡張することで、各フィールドの値を好きなように変換することもできる。例えば、

class IppaiHogeHoge(fields.Field):
    def _serialize(self, value, attr, obj):
        if value == "1":
            return value * 10
        else:
            return value

class HogeSchema(Schema):
    hoge = IppaiHogeHoge(required=True)
    foo = fields.Str(required=True)
    bar = fields.Int(required=True)

のように定義すると、dumpした結果は、

data, err = HogeSchema().dumps(hoge_data)
pprint(data)
pprint(err)
# '{"foo": "2", "hoge": "1111111111", "bar": 3}'
# {}

のようにできるので便利。

おわりに

上の方法とは別に、JSON -> class変換はmarshmallowでできるようなのだが、 class -> JSON変換の方法がまだわからないので、もう少し調べる。。。

Pythonプロフェッショナルプログラミング 第2版

Pythonプロフェッショナルプログラミング 第2版