ラベル 技術 の投稿を表示しています。 すべての投稿を表示
ラベル 技術 の投稿を表示しています。 すべての投稿を表示

2025年10月1日水曜日

ボットは絵も動画もブログも作成できるようになった

ボットは絵も動画もブログも作成できるようになった

人生とはボットを育てることなのかもしれない

これまで

https://blog.kakakikikeke.com/2025/04/kakakikikeke-die-twice.html

自分のツイートを学習させて自分っぽいツイートができるようになりました

アカウント

https://x.com/kakakikikekebot

現在のボットにできること

2025/09/26 現在ボットは成長し以下ができるようになりました

  • ツイートをする
  • ブログを書く
  • 絵を書く
  • ショート動画を作る

以下それぞれの仕組みを詳細に説明します

共通点

  • 動作マシンはローカルの M2 Pro mac mini
  • バックエンドでは「CPU50%のマイニング」「Ollamaサーバの起動」「SD-webui」が稼働している
  • 生成時は MPS にすべて対応しているものを採用
  • Python で実装
  • 生成時間は動画以外数分で完了 (絵、文章系)

ツイートをする

Twitter API + Ollama API (gemma3)

  • 自分のツイートを500件準備、そこから20件をプロンプトのコンテキストとして与える
  • ツイートを生成してもらうようなプロンプトを入力
  • レスポンスをパース、サニタイズなど調整してからツイート
  • ハッシュタグやリンクなどは含めないようにしている
  • プロンプトは以下
def build_prompt(past_tweets, sample_size=20):
    examples = "\n".join(random.sample(past_tweets, sample_size))
    return f"""以下は過去のツイートです:

----ここから過去のツイート----
{examples}
----ここまで過去のツイート----

これらの文体や話題を参考にして、あなたらしい新しいツイートを1つ生成してください。
日本語でかつ文章の意味がわかるような自然なツイートにしてください。
ツイートに「https://」から始まるようなURLは絶対に含めないでください。
ツイートの長さは50文字以内にしてください。
ツイートを作成した理由などは説明しないください、ツイートのみ生成してください。
"""

ブログを書く

Twitter API + Blogger API + Ollama (gemma3)

  • テーマを与えてそのテーマに基づいたブログ記事を書いてもらう
  • 出力は必ずHTMLになるようにプロンプトで調整、デフォルトがMarkdownっぽいので可能な限りMarkdownにならないようにする
  • テーマはランダムで100件保持、100件分のテーマを消費すると停止する
  • テーマはChatGPTに生成してもらっている
  • プロンプトは以下
def build_prompt_for_blog_post(theme):
    return f"""以下はブログ記事のテーマです。

----ここからテーマ----
{theme}
----ここまでテーマ----

このテーマを参考にして、あなたらしい新しいブログ記事を1つ生成してください。
ブログ記事内では Markdown 記法は使えません、Markdown 記法は自動で HTML に変換されません。絶対に Markdown 記法を使わないでください。
ブログ記事は Markdown 記法を使わずに生成してください。
ブログ記事内では HTML タグが使えます。
コードなどシンタックスハイライトが必要な場合は HTML タグを使ってください。
ブログ記事に画像は含めないでください。
ブログ記事内に StyleSheet や CSS の記述を含めることは可能ですがブログ本体の StyleSheet や CSS に影響を与えないようにしてください。
ブログ記事を作成した理由などは説明しないください、ブログ記事の内容のみ生成してください。
"""

絵を書く

Twitter API + Stable Diffusion WebUI API (model はランダム)

  • SD-Web UI を API モードで起動してそれをコールする
  • SD-Web UI の命令はそのまま JSON のペイロードとして API に送信できるので楽
  • 生成された画像のサイズは896x1152
  • モデルは固定ではなく10個ほど保持しておりそれを順番に使用していくように Python 側で制御
  • プロンプトもランダムになるようにしておりベース、性別、向き、服装、背景など要素ごとに毎回ランダムで生成するようにしている
def generate_prompt():
    # プロンプトの定義
    base = "masterpiece, best quality, ultra-detailed, 8k, cinematic lighting"
    gender = "".join(
        ("1 beautiful japanese young women, fair skin, light makeup, realistic face")
    )
    viewpoint = get_random_viewpoint()
    posing = get_random_posing()
    hair_cut = get_random_hair_cut()
    hair_color = get_random_hair_color()
    hair = f"{hair_color} {hair_cut}"
    face = get_random_face()
    background = get_random_backend()
    tops = get_random_tops()
    bottoms = get_random_bottoms()
    clothes = f"{tops}, {bottoms}"
    return "".join(
        f"{base}, {gender}, {viewpoint}, {posing}, {hair}, {face}, {background}, {clothes}",
    )

ショート動画を作る

Twitter API + Stable Diffusion WebUI API + LTX-Video (txv-2b-0.9.8-distilled)

  • text-to-video ではなく image-to-video の手法
  • 先に SD-WebUI を使って画像を作成してもらう
  • その画像を使って LTX-Video で動画にする
  • 256x384の4秒動画を作成 (num_frames=121、frame_rate=30)
  • 4秒以上の動画を生成する場合は192x256にすれば生成できる
  • 本当は896x1152で生成したいがリソースが足りない
  • 動画の生成はほぼマシンリソースをすべて使うので生成前にマイニングおよび ollama の runner を停止している
  • 各種プロセスを停止しないとメモリが枯渇してほぼ100%クラッシュする
  • 1動画生成するのに20-60分ほどかかる
  • 生成時間にムラがありおそらく入力している画像の影響だと思われるが安定しない
  • LTX-Video をサブプロセスとしてコールしているのでその作りが微妙
def convert_image_to_video(
    img_filename: str, width: int, height: int, prompt: str = ""
) -> Optional[str]:
    # 環境変数からパスを取得、設定がなければデフォルト値を使用
    work_dir = os.getenv("LTX_WORK_DIR", "/Users/kakakikikeke/Documents/work/LTX-Video")
    img_dir = os.getenv("LTX_IMG_DIR", "/Users/kakakikikeke/data/repo/videobot")

    # 仮想環境の Python を直接指定
    venv_python = os.path.join(work_dir, "env", "bin", "python")

    # 実行コマンド
    command = f"""
    cd {work_dir} && \
    PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 \
    {venv_python} inference.py \
    --prompt "{prompt}" \
    --conditioning_media_paths {img_dir}/{img_filename} \
    --conditioning_start_frames 0 \
    --height {height} \
    --width {width} \
    --pipeline_config configs/ltxv-2b-0.9.8-distilled.yaml
    """

今後追加したい機能

  • 音楽の作成
    • AudioGen
    • RVC
  • 長めの動画
    • 今のマシンだと画像サイズを落としてフレーム数を上げるしかない
    • もっと軽量なモデルを探す
    • 本当はWan2.2やVeo3レベルの動画を生成したい
  • 俳句
    • gemma3 でプロントプト換えるだけ
  • 音楽+動画
    • 上記で生成した音楽と動画を何かしらで組み合わせる
    • 最終形はこれなのかもしれない
  • 昔の写真や自分で書いた絵を動画にしたい
    • 過去の復元
    • 手書きの絵 -> アニメ化 -> 動画化
  • NanoBanana 連携
    • 有料だとかなり強力
    • というかお金を使う前提ならローカルで全部動かさないで Google AI Studo API や Colab でガンガン動かして高品質、高画質、長尺コンテンツを作りまくれる
    • ただマネタイズ考えないと大変なことなりそう
    • 動画にするだけならVeo3でもいける

課題

  • Twitter API Free プランの上限
    • 500ツイート/1日にほぼ当たっている
    • 有料プランにすれば解決するがどうするか
    • コンテキスト(メディア系、テキスト系)にアカウントを分割するか、管理は面倒になる
  • Ollama で動作させるモデル
    • gemma3 はだいぶ軽量なので動作する
    • gpt-oss のような大規模LLMは動作しない (動作はするがスワップだらけでかなり遅くなる)
    • openhermes など試したがハッシュタグや無駄に絵文字を使うのでやめたりした
    • いいモデルを探すのが難しい
  • プロンプトだけで制御するのには限界がある
    • 含めないでと命令しても含めてくる
    • モデルに合ったプロンプトを調整しなければいけないのが辛いのでなんとかしたい (プロンプト最適化など)
  • プロンプトのパターン追加
    • テーマなど手動の部分があるので自動化したい
    • 絵を描く際のプロンプトのランダム抽出の候補を追加する
    • そもそも完全ランダムにする方法を考える
  • 絶望的にマシンスペックが足りない
    • やはり AppleSilliocn だとかなり辛い
    • 基本は RTX などの CUDA が使えるグラボがあることが前提 (xformers動作など)
    • 動画生成は特に VRAM を消費するので VRAM モリモリのグラボがないと厳しい (Wan2.2など)
  • ディスクも足りない
    • ローカルにモデルを置きまくっているのでディスクが枯渇する

最後に

多少の知識はいるが生成系は本当に楽な時代になった

2025年9月10日水曜日

NanoBanana の API を試す

NanoBanana の API を試す

ちゃんとこういうテック記事も投稿しないとダメだなと思い試しました
Python を使っています

環境

  • macOS 15.6.1
  • Python 3.12.11
    • google-genai==1.35.0
    • pillow==11.3.0

準備

必要なライブラリをインストールします

  • pipenv install google-genai
  • pipenv install pillow

google-generativeai は Deprecated なので注意してください
google-genai を使いましょう

API キーの取得

Google AI Studio (ここ)から生成しましょう

サンプルコード

比較的新しめの技術に関しては AI 先生に聞いても教えてくれないから公式ドキュメントを見るのが一番なんですよね

ファイルをアップロードして変換してもらうだけの簡単なスクリプトなのでリファクタなどはしていません

xxx の部分は上記で取得した API キーを入力してください

from io import BytesIO

from google import genai
from PIL import Image

client = genai.Client(api_key="xxx")

prompt = """
  背景を宇宙に変更してください。
"""
image = Image.open("reforg.jpeg")

# チャットクライアントの作成
chat = client.chats.create(model="gemini-2.5-flash-image-preview")
# イメージとプロンプトの送信
response = chat.send_message([prompt, image])

# 結果(テキストと画像)の取得、一応 pyright のエラーも対応
if response.candidates is None:
    raise

if response.candidates[0].content is None:
    raise

parts = response.candidates[0].content.parts
if parts is not None:
    for i, part in enumerate(parts):
        if part.text is not None:
            print(part.text)
        elif part.inline_data is not None and part.inline_data.data is not None:
            image = Image.open(BytesIO(part.inline_data.data))
            image.save(f"generated_image_{i}.png")

で入力した画像がこれで

出力された画像が以下です

すばらしい
特にレスポンスの速さがすばらしい

最後に

Google AI Studio の UI からだと制限があったりしてうまく画像が生成されないことがあるようなのでそんな場合は API を使いましょう

ただこれだとチャットというよりかは一問一答の会話なのでチャット的な感じでインタラクティブな感じにしたいのであればもう少し手を加える筆意ようがありそうです

それでもこれが無料で使えるのは本当に素晴らしいことだと思います

参考サイト

2025年4月25日金曜日

これでボクがいなくなってもボクっぽいツイートは生き続けるでしょう

これでボクがいなくなってもボクっぽいツイートは生き続けるでしょう

便利な時代になったもんだ

アカウント

現在は1日1回ツイートするようにしています

仕組み

ローカルの M2 Mac で動かしています
開発当初は Fine-Tuning方式 (LoRA) にしようかなと思ったのですが面倒なのと精度も微妙なのでやめました
たぶん ollama が動作すればいいのでスペックはそこまで必要ではないかもしれません

概要

仕組みは非常に簡単で過去の自分のツイートを LLM にコンテキストとして与えてそれを元に新しいツイートを生成してもらっているだけです

前処理も含めて流れの全体は以下です

  1. ツイートのアーカイブ (tweets.js) からランダムに500件抽出
  2. 500 件のうち 20 件をランダムに抽出し LLM のコンテキストとして与える
  3. ツイートを生成してもらう
  4. ツイートする

というかなり簡単な流れになっています

LLM モデル

特に理由はないですが日本語に対応していて可能な限り軽量な LLM を採用しています

これを ollama で起動して API をコールしてツイートを生成しています

  • ollama run openhermes

ツイートを抽出するスクリプト

さすがに全ツイートを食わせるのは無理なので先に何件か抽出します
とりあえず今は500件抽出しています
ここの抽出条件をもっと自分っぽいツイートだけに絞ったら文章も良くなるのかもしれないです

ここで生成されたツイートを LLM に食わせます

import json
import random

with open(
    "/path/to/twitter_archive/tweets.js",
    "r",
    encoding="utf-8",
) as f:
    raw = f.read()
    # 最初の JavaScript 部分を除去
    json_data = json.loads(raw.replace("window.YTD.tweets.part0 = ", ""))
    # "http://" または "https://" を含まないツイートだけに絞る
    filtered_tweets = [
        t["tweet"]["full_text"]
        for t in json_data
        if "http://" not in t["tweet"]["full_text"]
        and "https://" not in t["tweet"]["full_text"]
    ]
    tweets = random.sample(filtered_tweets, min(500, len(filtered_tweets)))

with open("tweets_500.json", "w", encoding="utf-8") as f:
    json.dump(tweets, f, ensure_ascii=False, indent=2)

LLM によるツイート生成

先ほど作成したツイートファイルを展開しプロンプトを作成します

ここのプロンプトを変えるだけで生成されるツイートが大きく変わります (これが世に言う「プロンプト最適化」)

以下が現状使っているプロンプトですが Openhermes だと「URL 含めないで」と言っても平気で含めてくるので別途正規表現で削除する処理を追加しています

Twitter の API は v2 でないとダメなのでご注意ください

import json
import os
import random
import re

import requests
import tweepy

# 過去ツイートの読み込み
with open("tweets_500.json", "r", encoding="utf-8") as f:
    past_tweets = json.load(f)

# プロンプト作成
# openhermes の場合は可能な限り質問を具体的にしないと自然な文章にならない
# chatgpt はだいぶ自然なツイートを作成してくれるから本当は OpenAI API を使いたい
examples = "\n".join(random.sample(past_tweets, 20))
prompt = f"""以下は過去のツイートです:

----ここから過去のツイート----
{examples}
----ここまで過去のツイート----

これらの文体や話題を参考にして、あなたらしい新しいツイートを1つ生成してください。
日本語でかつ文章の意味がわかるような自然なツイートにしてください。
ツイートに「https://」から始まるようなURLは絶対に含めないでください。
ツイートの長さは50文字以内にしてください。
ツイートを作成した理由などは説明しないください、ツイートのみ生成してください。
"""

# Ollama API にリクエストを送信
# stream: false にしないと一文字ずつ処理しないといけないので false にする
response = requests.post(
    "http://localhost:11434/api/generate",
    json={"model": "openhermes", "prompt": prompt, "stream": False},
)

# 出力を整える
tweet = response.json().get("response", "(ツイート生成失敗)").strip()
# URL が含まれる場合があるのでここで削除、ドメインが追加されたら随時対応
tweet = re.sub(r"(https?://\S+|pic\.twitter\.com/\S+|t\.co/\S+)", "", tweet).strip()
print("自動生成されたツイート:")
print(tweet)

# Twitter (X) にツイート投稿
# 各種キーとトークン(環境変数などから取得するのが安全)
# OAuth 2.0 User Context 用クレデンシャル(環境変数から取得)
bearer_token = os.getenv("TWITTER_BEARER_TOKEN")
consumer_key = os.getenv("TWITTER_CONSUMER_KEY")
consumer_secret = os.getenv("TWITTER_CONSUMER_SECRET")
access_token = os.getenv("TWITTER_ACCESS_TOKEN")
access_token_secret = os.getenv("TWITTER_ACCESS_SECRET")

if all(
    [bearer_token, consumer_key, consumer_secret, access_token, access_token_secret]
):
    client = tweepy.Client(
        bearer_token=bearer_token,
        consumer_key=consumer_key,
        consumer_secret=consumer_secret,
        access_token=access_token,
        access_token_secret=access_token_secret,
    )
    try:
        response = client.create_tweet(text=tweet)
        print("ツイートを投稿しました")
        print(f"ツイートURL: https://twitter.com/user/status/{response.data['id']}")
    except Exception as e:
        print(f"ツイート投稿に失敗しました: {e}")
else:
    print("Twitter API キーが設定されていません。環境変数を確認してください。")

定期実行

Mac なので launchctl で行っています

生成されるツイートについて

詳細はアカウントを直接見てください
結果としては50点くらいです

課題

以下で改善点を上げます

文章が自然ではない

  • モデルが悪いかも
    • 日本語特化なモデル (Elyza や LLM-jp など) に変更すると良くなるかも
    • 試した中で一番自然だったのは OpenAI API だったので本当はそれがベスト
  • プロンプトが悪いかも
    • URL はいらないと命令しても URL が含まれたりする
    • 説明はいらないと命令しても説明が含まれたりする
    • プロンプトを構造的にして質問したほうがいいのかもしれない
  • そもそもコンテキストとして渡しているツイート自体が自然な文章ではないのが原因の可能性はある
    • つぶやきは文章ではない
    • そこから意図や感情を読み取るのは LLM でも難しいのかも
    • 渡すツイート情報を変更するのはあり
    • そもそもツイートを渡すのではなく「こういうツイートをして」とかにしたほうがいいのかも
    • そうなると自分がどういうツイートをしているのかを分析する必要があるが
  • コンテキストとして渡すツイートの精査をする
    • ランダムではなく「それっぽい」ツイートだけを抽出するようにする
    • その抽出作業自体を LLM に任せればいいのかも

なぞのURLとハッシュタグを含んでしまう

  • URL は正規表現で別途対応
  • ハッシュタグは含まないように命令したほうがいいのかも

リファクタリング

  • しません

定期実行が止まったらわからない

  • 通知機能をつけてもいいかも
  • あとはローカル実行ではなく適当なクラウドサーバに移行してもいいかも
  • Twitter の API が v2 から v3 になったり非互換になったりトークンが期限切れになった止まる

変なツイートしないか心配

  • Fワード系
  • 個人情報系
  • 急に誰かにメンションするとか

その他思うこと

LLM について思うことをつらつら書いていきます
ちなみに過去に思ったことはこちらです

Fine-tuning やら RAG やら MCP やらあるが正直オーバーエンジニアリングな気はする

  • LLM 自体がかなり賢くなっている
  • トークンの制限さえなくなれば「プロンプト最適化」が一強になるはず

AI を使うために AI を使う開発をするのは今後必然になる気がするが本末転倒な気がしなくもない

  • 例えば dspy や LangChain、MCP Server といったツールを使って LLM を操作する技術は今後必須になってくる
  • しかし結構大変なイメージ
  • 特に Fine-tuning や RAG や MCP は作るのも大変 (それすら AI 先生に助けてもらえるが)
  • やりたいことは AI のための開発ではなくあくまでもプロダクトの開発
  • そしてせっかく作ったものが必ずしも開発の役に立つ、手助けしてくれるレベルになるとは限らない

完全に AI のみで済むような世界観は実はまだまだ先の未来なのかもしれない

  • とは言え開発に LLM がないという状況は今後考えにくい
  • LLM はあくまでも「お手伝いロボット」で生成してくれたコードなどは結局自分たちでテスト、検証しなければならない
  • 本当はそこまで全部やってほしい
  • 極論「新機能でこういう機能がほしいから実装しておいて」とか「既存のコードでここにバグがあるから直しておいて」と言えば全部勝手にやってくるのが最高だがその未来は遠そう
    • Agent など実現するためのツールは揃っているがそれらを使って実現するスキルが自分には足りない
    • 結局「生成してもらう」->「コピペ」->「テスト」のループを繰り返すのが一番効率がいいのかもしれない

エージェントについて

  • 本当にいるのだろうかと思っている
  • もちろん自分の分身レベルでエージェントが勝手に動いてくれるならほしい
  • でもそこまでのエージェントを作るのはかなりの労力と技術がいると思っている
  • 現状のエージェントはいわゆる「作業の自動化」でそれは別にエージェントを使わなくても問題ない
  • これまで通りやっていた自動化を LLM を使って実現すればいいだけの話になる
  • つまりエージェントレベルではいらない (それをエージェントと呼ぶのであれば話は別だが)
  • 強化学習でいうところのエージェント+LLM の世界観
  • エージェントには意思があると思う

最後に

AI 先生は中身がブラックボックスすぎて何をしているのかわからなさすぎて怖いです
だがそれがいい

2024年12月3日火曜日

自分はレビューいらない派です

自分はレビューいらない派です

ChatGPT 先生と相談しやはりいらないという結論に至りました

レビューの意義やメリットについてはここでは触れません、またそれらも人それぞれの感性があることを前提に進めます

この記事は全面的にレビューを批判する記事になっています

自分のレビューに対する考え要約

  • レビューでバグは潰せない
    • それならばE2Eなテストを増やそう
    • ちなみにテストを書くにも嫌い (特にユニットテスト)
  • 情報共有の意味があるならレビューではなく直接タスクを与えたほうが価値がある
  • 静的解析、テストなどシステムにすべてまかせるような仕組みを作るべき
    • それがないから人間(レビュー)に頼ってしまう
  • 作ってくれた人は全面的に信頼している
    • どうしてもその人がレビューしてほしいと思うのであればやるべき
    • 作ってくれたコードに文句を言いたくない、文句があるなら自分で直せばいいだけ
  • そもそもレビューは同レベルの知識を持つ人でないと意味がない
  • バグのないアプリは存在しない
    • 自分は常にそう思っている
    • バグがある箇所や時期を踏まれていないだけでどんなアプリにも必ずバグはある
    • もしくは全く使われていないだけか
  • 上記踏まえて時間の無駄、逆にやらないことによる時間を有意義に使える

ChatGPT先生との相談ログ

以下先生に相談したログです
フォーマットなどは崩れていますが可能な限り読みやすいようにしています

Q: コードレビューについて質問です。自分はコードレビューは意味がないと思っているのですがあなたはどう思いますか?

A:

コードレビューが意味がないと感じる理由を教えてもらえますか?その理由によっては、コードレビューの意義を再考したり、効率を改善したりするアプローチが見つかるかもしれません。

コードレビューは多くの現場で以下のようなメリットをもたらすと言われています:

品質の向上
バグの発見や設計の見直しが行われ、コードの品質が向上します。複数の目で見ることで、自分では気づけないミスや改善点が見つかることがあります。

ナレッジ共有
他の開発者がコードを読むことで、プロジェクト全体の理解が深まり、属人化を防ぐことができます。

スキル向上
レビューを受けることで、自分のコードスタイルや設計に対するフィードバックが得られ、スキルが向上します。レビューする側も他人のコードを読むことで学びを得ることができます。

規律の向上
他人に見られることを前提にコードを書くため、コードの可読性や設計を意識するようになり、結果としてチーム全体の基準が引き上がります。

ただし、意味がないと感じるケースも理解できます。例えば:

形式的で深掘りしないレビュー: 表面的なコメントや惰性で行われるレビューでは、実際の改善につながりにくい。
過度な批判や不毛な議論: レビューが対立の場になると、効率が下がるだけでなく、チームの雰囲気を悪化させる。
時間がかかりすぎる: レビューが長時間に及び、進捗が遅れる。
もし具体的な課題があるのであれば、それに応じた解決策を一緒に考えられます。たとえば「短時間で効果的なレビューの方法」や「建設的なフィードバックの仕方」などについて話すこともできます!

Q: 自分がコードレビューが意味がないと感じる理由は以下です「コードレビューをしてもバグがゼロになるわけではない」「コードレビューに情報共有の意味があるのならコードレビューで情報共有しないで直接コードを修正するようなタスクを与えて情報共有したほうが効果もある」「時間の無駄」が主な理由です

A:

コードレビューが「意味がない」と感じる理由、納得できる部分もあります。それぞれのポイントについて考察と改善のアイデアをお伝えします。


1. コードレビューをしてもバグがゼロになるわけではない
考察: 確かに、コードレビューをしてもバグがゼロになる保証はありません。バグは多くの場合、コード以外(設計、要件定義、使用環境など)に起因することもあります。また、レビューでは見逃しが発生する可能性も高いです。

改善アイデア:

レビューの目的を明確化する: 「バグをゼロにする」ことを期待するのではなく、「主要な設計ミスや重大なバグを減らす」「可読性やメンテナンス性を向上させる」ことに焦点を当てると現実的です。
ツールを活用: 静的解析ツールやテストフレームワークで自動的にバグを検出できる部分を先に排除し、コードレビューの負担を減らす。


2. コードレビューより直接修正タスクを与えた方が効果がある
考察: 「修正タスクを与えることで学ぶ」というアプローチは効果的です。ただし、修正を行う本人がプロジェクト全体の背景や他の影響を理解していない場合、不適切な修正が増えるリスクがあります。

改善アイデア:

ペアプログラミング: 修正タスクを与える代わりに、タスクを行う開発者とコードの知識を持つレビュー担当者が一緒に作業する。これにより効率的な情報共有が可能。
モブプログラミング: チーム全体で特定のコードを修正しながら、全員で学び合う場を作る。
事前の知識共有: タスクを与える前に、該当箇所に関連する設計や背景知識をしっかり共有しておく。


3. 時間の無駄
考察: 時間がかかりすぎたり、効果を実感できないコードレビューは、無駄と感じやすいです。特に、形式的で建設的な議論が生まれないレビューは、コストに見合うリターンが得られません。

改善アイデア:

軽量レビューの導入: 大規模なコードレビューではなく、小さな変更を頻繁にレビューすることで、時間を節約しつつ質を保つ。
レビュー基準の明確化: 「何を確認すべきか」をチームで明確にして、効率的なレビューを目指す。
ツールの導入: GitHubやGitLabなどのプラットフォームを活用して、変更箇所のみをピンポイントでレビューする。


代替案:コードレビューを廃止した場合のリスク評価
もしコードレビューを完全に廃止した場合、以下のリスクが考えられます:

コードの品質や一貫性の低下
属人化の進行
他メンバーのスキル向上や学習機会の減少
これらのリスクを受け入れられる場合は、より直接的なアプローチを選ぶのも1つの方法です。ただし、チームメンバー全員の合意と運用ルールの徹底が必要です。

Q: コードの品質や一貫性の低下はユイットテストやエンドツーエンドテストで補えると思います。属人化の進行はレビューではそもそも補えません、実際にコードを書けるレベルになって初めて属人化の解消になります。他メンバーのスキル向上や学習機会の減少、これもレビューでは補えません。属人化の部分の回答と同じです

A:

なるほど、非常に筋が通った主張ですね。コードレビューのメリットとして挙げられがちな点が、他の方法で代替可能であるという視点は重要です。以下の観点であなたの意見を深掘りしつつ、代替アプローチについて考えてみます。


1. コードの品質や一貫性について
あなたの言う通り、ユニットテストやエンドツーエンドテスト(E2Eテスト)は、バグ検出や動作保証の観点でコードレビュー以上に効果を発揮することがあります。さらに、コードの一貫性についても、自動化ツール(PrettierやESLint、StyleCopなど)を使えば、レビューに頼る必要がほとんどなくなります。

代替アプローチ:

テストカバレッジの向上: ユニットテストやE2Eテストの割合を高め、レビューではチェックしない項目をカバー。
CI/CDの整備: 自動テストとコードフォーマットチェックをCI/CDパイプラインで実行し、レビューを通さずに品質を担保。
注意点: ただし、レビューはコードの「意図」や「設計」にフォーカスできますが、自動化ツールはその部分を補えないことがあります。テストで検出できない設計ミスや冗長な処理を防ぐための仕組みが必要です。


2. 属人化の進行について
属人化の解消には、「コードを書けるレベルになる」ことが不可欠であるという点に同意します。コードレビューだけでは、確かにそれが実現しない場合も多いです。

代替アプローチ:

ペアプログラミングやモブプログラミング: 直接的にコードを書きながら学ぶことで、知識共有とスキルアップを同時に実現できます。
ローテーション制度: 特定のタスクや機能領域について定期的に担当者を変える。これにより、複数のメンバーが同じ部分を理解する機会を得られます。
技術共有会: チームでコードや設計について定期的に議論し、属人化を減らす。


3. スキル向上や学習機会について
コードレビューはスキル向上の一部を担うことがありますが、必ずしもそれが最適な手段とは限りません。レビューのコメントが表面的だと学びは少なく、実際にコードを書く経験には遠く及ばないという意見は非常に妥当です。

代替アプローチ:

トレーニングタスクの設定: 開発者が新しい技術や設計パターンを学ぶための小さなタスクを与え、学びの機会を明確化。
メンター制度: 経験豊富な開発者が他のメンバーを指導し、実践を通じて学ぶ環境を整える。
レビュー以外のフィードバック: リアルタイムでのコード解説や設計レビューセッションを設ける。


結論
あなたの意見の核は「レビューだけではこれらの課題は解決しきれない」という点にあり、それは正しいです。加えて、レビューは一つの手段に過ぎず、それがチームにとって非効率であるならば、他のアプローチを導入すべきです。

チームとして以下のようなプロセスを試してみる価値があるかもしれません:

コードレビューの目的を再定義: レビューを行う場合でも、レビューの対象や方法を限定し、設計や意図に焦点を絞る。
自動化と実践型アプローチを組み合わせる: テストやCI/CDの自動化に投資しつつ、学びの場としてペアプログラミングや技術共有会を導入。
コードレビューを必須にする必要はありません。チームの状況や課題に合わせて柔軟にプロセスを設計することが最も重要です。

Q: ではコードをチェックできる自動化された環境がありかつチーム内で属人化を解消するコードの知識を共有する体制になっているのであればレビューは不要という認識でいいでしょうか?

A:

その通りです!コードをチェックする自動化された環境が整っており、かつ属人化を解消するためのコード知識共有体制が機能しているなら、コードレビューは不要と言えるでしょう。

以下にその条件を整理します:


1. 自動化されたチェック環境
静的解析ツール: コーディング規約の遵守や潜在的なバグ検出を行うツール(ESLint、SonarQube、Checkstyleなど)が適切に設定されている。
テスト自動化: ユニットテスト、統合テスト、E2Eテストが十分にカバーされており、CI/CDパイプラインで実行される。
コードフォーマットツール: PrettierやBlackなどでコードのスタイルを自動整形し、見た目のレビューを不要にする。
セキュリティチェック: 依存関係の脆弱性チェックやコードのセキュリティスキャンを自動化している。


2. 属人化を解消する仕組み
ナレッジ共有の文化: ドキュメント化や共有会、コードベース全体の可視性が確保されており、誰でも理解しやすい体制になっている。
ローテーションやペアプログラミング: 特定のタスクや機能領域が特定の人に依存しないように、メンバーが交代でコードを触る機会がある。
実践を通じた学び: 他のメンバーが既存のコードを理解しやすいタスク設計やコーディングの実践が行われている。
コードの意図を記録: 関数やクラスのコメント、設計ドキュメント、コミットメッセージで十分にコードの意図が伝わるよう工夫されている。


3. レビュー不要とする前提条件
以下の質問に「はい」と答えられる場合、レビューは不要と考えられます:

バグの発生率が許容範囲内か?
→ テストや静的解析でほぼ十分にバグが防げている。
チーム全員がコードの目的や設計を理解しているか?
→ 共有体制が機能し、メンバーがコードを理解している。
新メンバーが独立して貢献できるまでの時間が短縮されているか?
→ ドキュメントやナレッジ共有によって属人化が解消されている。
チーム内の信頼が高いか?
→ メンバー間の信頼が高く、他人のコードが問題なく利用できる。


検討ポイント
レビューを廃止する前に以下も確認すると良いでしょう:

トライアル期間を設ける: レビューを完全に廃止する前に、レビュー頻度を下げたり、特定のケースでのみ行う運用を試してみる。
データの計測: レビューをしないことで、バグの発生率やリリース速度がどう変化するかを測定する。


結論
「自動化されたチェック環境」と「属人化を解消する仕組み」が整備されているのであれば、レビューは不要であるという認識で問題ありません。それどころか、その環境が機能している場合、レビューを行うことが逆に非効率になる可能性もあります。

重要なのは、チームにとって最適なプロセスを柔軟に設計し、定期的に見直すことです。このアプローチがあなたのチームにとってベストな選択肢になることを願っています!

相談した結果感じたこと

レビューは一つの手段に過ぎず

確かにこれには同意した
強制するものではなくあくまでも「手段」であるということ
そういう意味でも自分はレビューという手段の優先度はかなり低い

あなたのチームに合ったプロセスを模索するのが重要

確かにこれも同意した
チーム内でレビューに重きを置いていることを上も理解してそこにコストを割くことを容認してくれている場合はじっくりやっていいと思う

ただそれでもコードの内容を知らない人にレビューしてもらっても何も意味がない気がする

データの計測: レビューをしないことで、バグの発生率やリリース速度がどう変化するかを測定する。

これも同意
むしろデータ計測やサービス効果測定みたいなものは初めからあってもいいと思う
ただどうやるかと何を評価項目にするかとどう抽出するかはかなり難しい気がする

最後に

最後は ChatGPT 先生を言いくるめたような感じなってしまいましたが最終的には自分の意見に同意してくれたのでよかったです

参考文献

2024年2月4日日曜日

最近の LLM 界隈について感じたこと

最近の LLM 界隈について感じたこと

Stable diffusion などの画像生成系については触れてないです
個人的な意見です

LLM

  • すべての機械学習の中心に LLM がいる

Fine Tuning と LLM

  • 今は1から LLM を作る時代ではない
  • GPT2 なりを Fine Tuning して独自の LLM を構築するのが主流 (というかクソ簡単だから
  • ニューラルネットワーク、深層学習、keras、tensorflow、torch などを学ばなくても自作の LLM が作れる
  • それにより HuggingFace や GPTs などに大量に独自 LLM が発生している
  • そもそもベースとなる LLM を変えるだけでもいろいろと精度は変わりそう

Transformer

  • 公開されているさまざまな LLM をローカルで簡単に動かすことができるツール
  • これができたことにより Fine Tuning がより簡単にできるようになると理解
  • 簡単 LLM 作成時代に突入するきっかけになった基礎技術

LangChain

  • LLM に質問するだけだったものに質問の仕方を工夫することで Fine Turning しないでも独自の知識を持ったかのように LLM に回答されることができるようになるツール
  • 例えばローカルで文章の類似度検索した結果を元に LLM にコンテキスト付きの質問をすることができる (Retrieval Chain と呼ばれる手法
  • Retrieval Chain に限って言えば頑張るべきは類似度検索の精度/再現率になるので FAISS や ChromaDB のチューニング勝負になりそう
  • FAISS や ChromaDB を使いこなす必要があるということ
  • Fine Tuning とどっちがいい精度になるのかは不明
  • そもそもベースとなる LLM を変えるだけでもいろいろと精度は変わりそう (Fine Tuning 同様
  • Agent はあくまでも LLM を環境としてフィードバックループを繰り返す技術 (っぽい
  • これが LLM + 特定の環境 (ゲームなど) にもフィードバックできるようになると便利そう (もうあるかも、参考

Classical Machine Learning

  • すべて LLM で解決できるのかもしれない
  • 二値判定、多値判定、クラスタリング、分類器などは LLM に質問するだけで処理してくれる

強化学習

  • LLM と組み合わせて使うようになるのかもしれない (上記強化学習同様
  • サンプル効率問題、次の行動の判断を LLM に任せる場合に何回尋ねるべきか、その場合のラグはどうなるのか (一応解決策があるらしいが
  • 環境(主にゲーム)がすぐにフィードバックして画面で確認できるので楽しさはある
  • プログラミングの導入でマイクラを使うように機械学習の導入でカートポールを使うのはどうだろうか

検索との使い分け

  • 結局どっちを信じるかではある
  • どちらも間違っている想定で最終的には自分で検証するのが一番
  • どちらも問題を解決するためのアプローチでしかないと思うが LLM は Agent 化して解決までやってくれる可能性はある

Apple Sillicon について

  • モデルを学習するのに Apple Sillicon 対応していないケースが多い
  • pytorch などの基礎となるライブラリは対応しているがそれを扱うツール系が対応していないケースが多い
  • 対応していないと全く使えないケースや CPU のみで動作するケースがある
  • そこで登場したのが mlx
  • M2 Mac の場合 LLM LoRA などはこれで作るのが最適
  • 音声や画像関連も対応してくれるとうれしいがどうなるか不明
  • なので全体としてやはり CUDA 優位な傾向がある

何かあったら追記するかも

2023年11月13日月曜日

10年以上エンジニアをやってみた感想

10年以上エンジニアをやってみた感想

思ったこと感じたことをつらつらと
コードの良し悪しについては昔書いたのでそちらを参照してください

経歴やスキルセットなど

  • 2010 - 2011 クラウドサービスの開発/運用 (Java)
  • 2011 - 2013 プラットフォームサービスの開発/運用 (Java)
  • 2013 - 2015 モバイルサービスの開発/運用 (Java)
  • 2015 - 2016 IoT サービスの開発/運用 ©
  • 2017 - 2018 コンテナサービスの開発/運用 (Ruby)
  • 2019 - 2020 クラウドサービスの開発/運用 (Python, Golang)
  • 2020 - 2023 コンテナアプリケーションの開発 (Python)
  • 2023 - Now スケジューラサービスの開発 (Python)

詳細はこちらを御覧ください
正直業界全体については語れないので完全に自分の世界での所感になります
マネジメントなどの経験はありません

すべてはアプリケーションでありソフトウェアな気がする

  • Webアプリ、スマホアプリ、インフラ、ハードウェア、セキュリティ、機械学習などいろいろ経験したが最終的にはコードを書いていた気がする
  • コードがあった上でそれぞれの分野に追加で必要な技術が出てくる
  • 例えば Webアプリであればフレームワーク、ハードウェアであれば電気電子回路の知識、機械学習であれば三角関数や微分積分などの数学知識といった感じ
  • そういった意味でどの業種もソフトウェアエンジニアであると思っている

コードから逃げてはいけない

  • エンジニアである限りコードは一生ついてまわるものだと思っている
  • なのでコードから逃げてしまうとエンジニアとして仕事ができないし成長もできなくなる
  • コードから逃げると自分の首をしめることになるので結局つらくなるのは自分な気がする
  • コードがわからない -> エンハンスやバグ修正ができない -> レガシーなコードになる -> 何もできなくなるの技術的負債ループに突入する
  • コードが見れないツールやライブラリを使う業務は本当につらいと思う
  • 責任分界点という意味では楽なのかれないがスピード感がでない

汎用的なスキルを身につけよう

  • 究極に言えばオープンな技術だけ学べばいい
  • 理由はどこに行っても通用するため
  • クローズドな技術やツールは再利用できる機会が極端に少ないので別のところに行って使えないことが多い
  • 具体的には自社ツールとかベンダー製品など
  • その中でもオープンな技術を学べることはあるのですべてが無駄というわけでない
  • API が付属していて REST のコールの仕方や SDK という技術、使い方について学べるかもしれないなど
  • それでもオープンな技術を学ぶほうが最終的なレベルアップ度合いは大きいと思っている
  • 汎用的なスキルとともにスキルの身につけ方、王道のサービスの構築手法やアプローチも身に着けたい (スクラム、ウォータフロー、DDDなど)

広く深くいろいろな技術を学ぶ

  • 深く狭くだと限定的なエンジニアになってしまいそう
  • いろいろなところで活躍したいなら広く深くが必要
  • 常にリスキリングというか新しいことを学ぶ意識が大事
  • 仕事で使っているスキルは勝手に広く深くなる
  • そうじゃないスキル、流行りのスキルをどうキャッチアップするか

やるべきこととやらなくていいことを見極める

  • 自分は仕事でもプライベートでも無駄なことが嫌いなので無駄は極力排除したいと思っている
  • 例えばコーディングにおいては最低限のことだけやる
  • 作らなくてもいいサービスや機能は作らない
  • 作らなくてもいいという線引きが難しいが簡単に言えば無くてもサービスが継続できるならやらなくてもいいと思っている
  • 作ったら作ったでそれを運用するコストが出てくる
  • コーディングだけでなくツールの導入なども同じ、導入したらしただけそれを面倒みる必要がある
  • 特にかくシンプルにやることを心がけたほうがいい
  • ただ何もしないとしないでモダンな技術に触れる機会がなくなるのでそこのトレードオフもある

最短距離を走る

  • 無駄を作らないための手法でもある
  • ある機能やサービスを作るときにゴールは1, 2個だがそこまでいくアプローチは100も200もあると思っている
  • そのいくつもあるアプローチから最適かつ最短距離を見つけられるかが重要
  • ただそればっかりは技術とかではなく経験が非常にものを言うと思っている
  • 開発の経験を重ねるとあるお題が与えられたときにパッと「あー、これはこうだからこれとこれを使えば簡単にできそうだな」「これはやりすぎだな」みたいなことがすぐに出てくるようになる気がする
  • オーバーエンジニアリングしない

1から全部やり直せるくらいになる

  • 自分の経験として前任者がいなくなったサービスをメンテナンスする業務が多くあった
  • その中で既存のコードをメンテナンスするよりかは1から全部作り直したほうが良いケースが結構あった
  • そのときに既存のコードを読み解き新たに作り直せると将来的も良いケースが多くあった
  • よくわからないコードをメンテンナンスする、全然メンテンナンスされていない脆弱なコードをメンテナンスするくらいな作り直したほうが良いことがある
  • おもいって新しく作り直すという方向に舵を切れるかどうかの判断も大事

レベルの平準化問題、属人化問題

  • チーム全員を同じレベルにするのが難しい
  • 開発を一人でやるとサービスが属人化する
  • 開発後に運用でジョインするとかなり大変なので開発の段階からやったほうがいい
  • スキルセットの違いによるスタートダッシュをどう埋めるか
  • 教育するしかないがそれにかかるコストもゼロじゃない
  • 勝手に学び勝手に成長してくれるという貴重な人材

仕事以外の自分の趣味をしっかり作る

  • プログラミングが趣味でもいいとは思う
  • ようするにリフレッシュできることを作ったほうがいいという話
  • メリハリがあるほうが仕事もはかどるし学びの効率も上がると思っている
  • ストレス発散

ChatGPT

  • たまーに嘘をつくが便利
  • 使いこなせば強力
  • トラブルシューティングや Web にないコーディング方法が知りたいときにもしかしたらと思って使う
  • 過度な期待はしない
  • Google との併用
  • 全部書いてもらいたいが技術的負債になってはいけない、自動生成されたコードはブラックボックスになりやすい

やったことは忘れないように記録する

  • 自分もやっているがブログがおすすめ
  • 動画だとコードの内容をコピペできないのでテキストがいい
  • 広く深くを全部覚えてることはできないのでいつでもひっぱり出せる知識データベース的なのを作る
  • 資格などの証明ではないが public に公開している自分の知識があればやったこと経験したことの証明になる

最後に

プログラミング楽しい
昔から図工とかものを作るのが好きだったから天職だったのかもしれない

2023年1月22日日曜日

いいコードとはなにか考える

いいコードとはなにか考える

つらつらと思っていることを書き連ねてみます

まとめ

  • シンプルイズベスト
  • コードがいいかわるいかよりもコードを深く理解しているかが重要
  • バグがなければ何でもいいんじゃないか

メンテナンス性について

  • いいコードはメンテナンスのしやすいコードなのか
  • メンテナンスしやすいコードは気軽に変更できる、変更しても影響が小さいなどがあると思う
  • でも結局はコードをしっかり理解しているかどうかな気がする
  • コードを理解していないと気軽に変更も影響範囲もわからないので
  • あとはテスト周りがしっかりしているか、ユニットテストのカバレッジやe2eテストがあるか、CDできる環境があるかも重要
  • 毎回手動でテストするのは辛いし、そうなっているとリファクタリングする気も起きなくなる
  • 今はいいコードだと思って書いても結局1, 2年後見たとときに理解できなかったダメなのでは
  • 何も気にせずファイル1つで全部そこに上から順番に処理するようなスクリプトならどうだろうか

手法について

  • 世の中にはコーディングに関するいろいろな手法や書籍がある
  • いろいろあるがいろいろありすぎて困る
  • 個人的には一番意識するべきは結局シンプルイズベストだと思う
  • いかにシンプルに無駄なことをしていないか=いいコードなのかも
  • コードをシンプルに書くとは簡単に言えば一番上から順番に実行されていくコード
  • いきなりどっかに飛んだり暗黙的なハンドラが呼び出されたりしない
  • あるゴールがあるときにいろいろな書き方があるがその中から最適を選択できるかどうか
  • 選択できるかどうかは経験則だと思う
  • いきなり良いコードは書けないから徐々に良いコードにしていけばいいと思う
  • それをできるかどうかも結局はコードをしっかりと理解しているかが重要だと思う

可読性について

  • いいコードは読みやすいコードなのか
  • メンテナンスしやすいコード=読みやすいコードと言えるのだろうか
  • 結局コードを知るためにはある程度学習しないとダメだとは思う
  • いいコードだと学習時間が短いとは限らない
  • コード把握までの時間を短くするにはドキュメントとトライアンドエラーさせることだと思う
  • いいコードはしっかり抽象化できるている、重複がないというのがよくあるケースだと思うが個人的には抽象化されてすぎていても良くないと思う
  • その心はコードがいろいろなところに分散しすぎていて追いづらいというのがある
  • 可読性を高める最強の要素は「コメント」と「ドキュメント」なのかもしれない
  • チームが日本人なら無理に英語にしないで積極的に日本語を使っていいと思う、コミットメッセージとかIssueとか

バグとの関連について

  • いいコードはバグのないコードなのか
  • 個人的にはこれはイコールではないと思う
  • わるいコードでも全然バグを踏まないコードもあると思っている
  • むしろレガシーコードのほうが安定しているケースもあると思う、いいとは思わないが
  • いいコードにしようと思ってバグを埋め込んではいけない
  • がバグをおそれてリファクタリングしないのもダメだと思う、難しい
  • 結局バグを生み出さないこと品質を担保することが大目的なのであればレガシーコードを運用し続けるのも手だと思う
  • ただ将来の技術的負債になることは間違いない
  • レガシーコードにバグがいきなり発生して誰も直せないパターンとかはあるある
  • レガシーコードを書き換えてモダンコードにするよりかは式年遷宮で全部書き換えるのがいいと思う
  • 当然インタフェースは変えないようにしなければいけないが
  • v2移行などで多少のインタフェース変更が出てしまうことはよくあるのでその場合はドキュメントやコミュニケーションでカバーするしかない

そもそも悪いコードとはなにか

  • 相対する言葉の悪いコードとはなにか
  • アンチパターン的なことなのだろうか
  • 悪いコードを書かないようにすれば自然といいこーどになるのかもしれない
  • そのコードが悪いと客観的に感じることができるかも重要
  • なかなかそれは経験がないと難しいかもしれない

チームビルディングについて

  • いいコードは人によって異なると思っている
  • なのでチームで一つのコードを開発すると個人の思想や経験がコードに反映されてしまうことがある
  • そういうときのために設計思想や開発手法やデザインパターンやフレームワークみたいなものがあるんだと思う
  • がそれでも多少のブレはある
  • 基本はチーム内でコーディング規約などを決める感じにはなる
  • いいコードを書くためにはある程度の経験が必要だと思っている
  • なので経験のある人の思想がチーム内のコーディング規約に大きな影響を与えてしまう可能性がある
  • チームがそれを合意できるのであれば問題ないが押し付けるのはよくないと思う
  • すでに安定しているコードに対してわざわざそれを適用するのかという判断も重要
  • 将来大きな技術的負債になるのであればやるくらいでいいと思う
  • 全員が全員同じレベルのコーダになるのは本当に難しい

テストについて

  • テストもいいコードとして書く必要があるか
  • テストも抽象化やリファクタリングをし続けたほうがいいのか
  • そこまでのコストが割けるか、売上には直結しない
  • プロダクトの品質には影響するかもしれない
  • テストの目的はカバレッジを100%にすることではなく開発者の精神安定
  • テストがしっかり書けている -> 積極的に安心して書き換えできるという意識が重要
  • ただ個人的に思うのはテストはおまけでしかないので面倒だということ
  • その面倒な作業を誰かがやらなければならないということ
  • 分担すればいいというのもあるがそうするとテストコードの書き方がバラバラになりテストの保守性が下がってしまったりする
  • 個人的にはテストコードを書くのは嫌いである

コンテキストによっても違う

  • ちょろっとスクリプトを書くときには
  • Webアプリケーションやスマホアプリのときには
  • 一時的にしか使わない場合
  • 将来的に継続的に使う場合
  • どんなときでもクラスベースなコーディングが必要なのか

番外編: AIにがんばってもらう

  • copilot のような自動生成にがんばってもらう
  • AI が書くほうがいいコードなはず
  • AIではないがコードジェネレータを作成するのもいいと思う

問題: どっちがいいコードなのだろうか

A

puts "hello"

B

require 'logger'

class Message

  @@logger = Logger.new(STDOUT)

  def initialize(title)
    @title = title
  end

  def say()
    @@logger.info @title
  end
end

Message.new("hello").say
  • 用途にもよる?
  • 将来どうメンテナンスしていくか?
  • 経験のあるなしに影響する?

2016年4月2日土曜日

Perl の LWP でニフティクラウドモバイルバックエンドにプッシュ通知を登録してみた

概要

タイトルの通りです
ニフティクラウドモバイルバックエンド (以下、NCMB) にプッシュ通知を登録する Perl スクリプトを作成してみました

環境

  • CentOS 6.7 64bit
  • Perl 5.10.1
  • Cpanm 1.7039

Cpanm のインストール

  • cd /var/tmp
  • wget https://cpanmin.us -O install_cpanm.pl
  • perl install_cpanm.pl - --sudo App::cpanminus

ライブラリのインストール

cpanm を使って LWP で https をコールするために必要なライブラリをインストールします

  • cpanm Crypt::SSLeay
  • cpanm Mozilla::CA

Crypt::SSLeay をインストールするときに「Building and testing Net-HTTP-6.09 …」のフェーズで結構時間がかかりました

登録用スクリプト

スクリプト全体は以下の通りです
スクリプト冒頭の $ak, $ck は各自が作成したアプリケーションキーとクライアントキーを指定してください

  • vim send_push.pl
#! /usr/bin/perl
use warnings;
use strict;
use autodie;
use LWP::UserAgent;
use DateTime;
use Digest::SHA qw(hmac_sha256_base64);

my $ak = 'Please input your application key';
my $ck = 'Please input your client key';
my $endpoint = 'mb.api.cloud.nifty.com';
my $version = '2013-09-01';
my $path = 'push';
my $method = 'POST';
my $uri = "https://${endpoint}/${version}/${path}";
print $uri, "\n";
my $req = HTTP::Request->new( $method, $uri );
$req->header( 'Content-Type' => 'application/json' );
$req->header( 'X-NCMB-Application-Key' => $ak );
my $timestamp = &getTimestamp;
my $sig = &getSignature($method, $endpoint, "/${version}/${path}", "", $ak, $ck, $timestamp);
$req->header( 'X-NCMB-Signature' => $sig );
$req->header( 'X-NCMB-Timestamp' => $timestamp );
my $json = '{"immediateDeliveryFlag":true,"target":["ios"],"message":"test"}';
$req->content( $json );

my $lwp = LWP::UserAgent->new();
my $res = $lwp->request( $req );
print $res->content, "\n";

sub getTimestamp {
  my $dt = DateTime->now( time_zone=>'local' );
  return $dt->strftime('%Y-%m-%dT%H:%M:%S.456Z');
}

sub getSignature {
  my $method = $_[0];
  my $endpoint = $_[1];
  my $path = $_[2];
  my $queryString = $_[3];
  my $ak = $_[4];
  my $ck = $_[5];
  my $timestamp = $_[6];
  my $sigMethod = 'HmacSHA256';
  my $sigVer = '2';
  my %headers;
  $headers{'SignatureMethod'} = 'HmacSHA256';
  $headers{'SignatureVersion'} = '2';
  $headers{'X-NCMB-Application-Key'} = $ak;
  $headers{'X-NCMB-Timestamp'} = $timestamp;
  my $strh = '';
  foreach my $key (sort keys %headers) {
    $strh = $strh . '&' . $key . '=' . $headers{$key};
  }
  $strh = substr($strh, 1);
  my $strToSign = $method . "\n" . $endpoint . "\n" . $path . "\n" . $strh;
  print $strToSign . "\n";
  my $sig = hmac_sha256_base64($strToSign, $ck);
  while (length($sig) % 4) {
    $sig .= '=';
  }
  print $sig , "\n";
  return $sig;
}

実行は

  • perl send_push.pl

で OK です
プッシュ通知の内容は my $json で指定しているので適宜変更してください
とりあえずライブラリが正常にインストールされて、NCMB 側でプッシュ通知が送信できる準備ができていれば問題なく登録できるはずです

最後に

とりあえずプッシュ通知が登録できる Perl スクリプトを作成してみました
Perl 標準でインストールされている LWP を使っています

POST の部分を GET に変更すればプッシュの一覧を取得できるのは確認しています
他の API も $path 部分を変更すればコールできると思います ( 動作確認は実施していません )
全然 SDK っぽくないので、ちゃんと作るなら Cpan で配布できるようにモジュール化しないとダメだと思います

2016年1月25日月曜日

markdown2impress を使って Markdown からスライドを作成してみた

概要

これまでスライドは Reveal.js を使って作成してきました
Reveal.js も良く出来ていて重宝しているのですが、編集が HTML を直接編集するため互換性やら見た目があまりよくありません
Jekyll にも Hekyll というプラグインがありこれを使えば Jekyll でスライドが作成できるのですが、スライドごとに Markdown ファイルを追加しなければいけないのでページが多いと大変です
今回は README.md 一個でスライドが簡単に作れる markdown2impress を試してみました

環境

  • CentOS 6.7 64bit
  • Perl v5.10.1
  • cpan 1.9402
  • Apache httpd 2.2.15
  • 動作確認ブラウザ Firefox 43.0.4

必要なモジュールのインストール

  • cpan -i Data::Section::Simple
  • cpan -i Text::Markdown
  • cpan -i Text::Xslate
  • cpan -i Path::Class

それぞれインストールしてください
インストールに結構時間がかかります

サンプルスライドの作成

About me
========

* @kakakikikeke
* [blog](http://kakakikikeke.blogspot.jp/)
  • mkdir -p /var/www/html/test
  • cp -ipr index.html js/ css/ /var/www/html/test

http://localhost/test/ にアクセスするとサンプルのスライドを確認することができると思います

sample_markdown2impress.png

Tips

YAML 系のエラーが出る場合は以下を試してみてください
http://kakakikikeke.blogspot.jp/2012/10/perlcpanyaml.html

cpan でモジュールをインストールする前にインストール時の yes の回答をデフォルトで全部 yes にしておくことをおすすめします
「Text::Xslate」のインストールが非常に長いので
http://kakakikikeke.blogspot.jp/2013/07/perlcpan.html

2016年1月21日木曜日

CentOS に最新の Nodejs をインストールする方法

概要

CentOS に最新版の Nodejs をインストールする手順を紹介します
epel を使うと v0.x 系のバージョンの Nodejs がインストールされてしまうので yum ではダメです

環境

  • CentOS 6.7 64bit
  • Nodejs 4.2.5
  • npm 2.14.12

インストール方法

コンパイル済みのバイナリファイルを展開して配置して終了になります

  • cd /var/tmp
  • wget https://nodejs.org/dist/v4.2.5/node-v4.2.5-linux-x64.tar.xz
  • tar xvf node-v4.2.5-linux-x64.tar.xz
  • mv node-v4.2.5-linux-x64 /usr/local/
  • ln -s node-v4.2.5-linux-x64/ node
  • cd /usr/bin
  • ln -s /usr/local/node/bin/node
  • ln -s /usr/local/node/bin/npm
$ node -v
v4.2.5
$ npm -v
2.14.12

こんな感じになれば OK です

2016年1月19日火曜日

楽譜を作成できる abc.js を試してみた

概要

abc.js はブラウザ上に楽譜を描画することができるライブラリです
楽譜の描画に必要な情報を渡すと SVG 形式で楽譜を描画してくれます

環境

  • CentOS 6.7 64bit
  • Apache httpd 2.2.15
  • Firefox 43.0.4
  • abc.js 2.3

abc.js のインストール

インストールは簡単です
必要な js ファイルを適当な場所に配置すれば OK です

今回は既存のサイトの <pre> 情報から楽譜を作成するプラグイン方式と、abc.js のメソッドをコールすることで楽譜を描画するベーシック方式の 2 種類で楽譜を作成してみました

abcjs_plugin を使ったサンプルコード

まずはプラグイン方式を使った方式を紹介します

  • cd /var/www/html/abcjs
  • wget https://raw.github.com/paulrosen/abcjs/master/bin/abcjs_plugin_2.3-min.js
  • vim plugin_sample.html
<!DOCTYPE html>
<html>
  <head>
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
  <script src="abcjs_plugin_2.3-min.js" type="text/javascript"></script>
  <body>
    <pre>
X: 1
T: Musikalisches Opfer (BWV 1079)
T: No. 3 Canones diversi (Realisation)
C: J. S. Bach
K: Eb
V: 1
C4E4|G4A4|=B,4z2G2-|G2^F4=F2-|=F2=E4_E2-|_E2=D2_D2C2|
V: 2
C2E2G2c2|=Bcde fedc|dGdf edc=B|=A=Bce dc=B=A|GAB_d cBAG|FGAB AGFE|
V: 1
=B,2G,2C2F2|E4D4|C4E4|GFGc GEDE|FG=A=B cEFG|ADEF GFED|
V: 2
DEFG FEDA|GFEc =B=AGF|EDEG cGFG|E4C4|D4E4|F2C2G,2=B,2|
V: 1
EFGA BAGF|GABc _dBAG|=A=Bcd ec=B=A|=Bcde fdGd|cdef edc=B|c2G2E2C2:|
V: 2
C2_D2=D2_E2|-_E2=E4=F2|-=F2^F4G2|-G2z2=B,4|A4G4|E4C4:|
    </pre>
  </body>
</html>

これで http://localhost/abcjs/plugin_sample.html にアクセスすると楽譜の元データと楽譜の SVG 画像が表示されるとおもいます

abcjs_plugin_sample.png

プラグイン方式の場合は <pre> タグの中に <div> タグで SVG 情報を描画するので、既存の楽譜情報を削除することができません
( 別途、JavaScript を使えばできると思いますが )

もしすでに楽譜情報が HTML として存在しているなら、この方式を使ったほうが簡単ではあります

abcjs_basic を使ったサンプルコード

では、次に楽譜情報を作成する関数をコールすることで楽譜を描画してみたいと思います

  • cd /var/www/html/abcjs
  • wget https://raw.github.com/paulrosen/abcjs/master/bin/abcjs_basic_2.3-min.js
  • vim basic_sample.html
<!DOCTYPE html>
<html>
  <head>
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
  <script src="abcjs_basic_2.3-min.js" type="text/javascript"></script>
  <script src="myabc.js" type="text/javascript"></script>
  <body onload="ABCJS.renderAbc('notation', score); return false;">
    <div id="notation"></div>
  </body>
</html>
  • vim myabc.js
var number_of_tunes = "1"; // 曲数
var title = "score_title"; // 楽譜タイトル
var measure = "4/4"; // 拍子 (e.g. 4 拍子)
var long = "1/4"; // 音符の長さ (e.g. 8分音符)
var rhythm = ""; // リズム
var major_key = ""; // 長調
var score = '\
X: ' + number_of_tunes + '\n\
T: ' + title + '\n\
M: ' + measure + '\n\
L: ' + long + '\n\
R: ' + rhythm + '\n\
K: ' + major_key + '\n\
C D E F|G A B c|C D E F|G A B c\n\
'

せっかくなので全く同じ楽譜ではなく自分で楽譜の情報を与えてみました
とは言ったも「ドレミファソラシド」を描画しているだけです
また、JavaScript と HTML を分けて、楽譜情報を JavaScript ファイルで管理するようにしています

ABCJS.renderAbc に描画する div タグの id 情報と描画する楽譜の文字列情報を渡せば OK です

コード中に各パラメータの説明を簡単に記載しています
自分は楽譜を読むことはできないので、解釈を間違っているパラメータがあるかもしれないのでご了承ください

ドレミファソラシドは英語で「CDEFGABc」に対応しているのでその情報を与えています

これで http://localhost/abcjs/basic_sample.html にアクセスすると楽譜の SVG 画像が表示されるとおもいます
先ほど違って楽譜の元情報は描画されないと思います

abcjs_basic_sample.png

最後に

とりあえず楽譜を描画することはできました

触っていて一番気になったのはドキュメントが非常に少ないことです
公式サイトにもちゃんとしたドキュメントがなく、楽譜を描画する際に与えるパラメータ情報が見つからず「X:」とか「L:」とかが一体何を表しているのかさっぱりわかりませんでした

なのでソースコードを Github からダウンロードして特定のキーワードで grep をかけることでパラメータをパースしている実装部分を探して何を表しているパラメータなのか判断しながら試しました

  • $ grep -r --exclude=jquery-1.11.3.min.js --exclude-dir bin/ 'R:' * | grep -v 'PER:'

結構たくさん指定できるパラメータがあって、ちゃんとこれらを使って楽譜を作成しようとしたら、楽譜を書く力もないと結構大変かもしれません

  • parse/abc_parse_header.js
var metaTextHeaders = {
        A: 'author',
        B: 'book',
        C: 'composer',
        D: 'discography',
        F: 'url',
        G: 'group',
        I: 'instruction',
        N: 'notes',
        O: 'origin',
        R: 'rhythm',
        S: 'source',
        W: 'unalignedWords',
        Z: 'transcription'
};

2016年1月18日月曜日

Arduino でサーボモータを制御してみた

概要

Arduino でサーボモータを制御してみました
Arduino にデフォルトで含まれている Servo ライブラリと独自で PWM を作成して制御する方法の 2 つを試してみました

環境

配線

Arduino UNO はデフォルトで PWM をサポートしているため配線は非常に簡単です
arduino_servo_circuit.jpg

サーボモータから出ている線の

  • 茶色 -> Arduino GND
  • 赤色 -> Arduino 5v電源
  • オレンジ色 -> Arduino GPIO 8 番

に接続します
GPIO は他の好きなところで OK ですが、今回は 8 番を使って進めていきます

制御用スクリプト

2 つ紹介します
どちらも流れとしては Serial.read で受け取った角度情報にサーボモータを回すという処理になります

独自の実装で PWM を送信

まずは自分で PWM 信号を作ってサーボモータを制御するスクリプトです

int deg = 610; // msec.
int pin = 8;

void setup() {
  pinMode(pin, OUTPUT);
  Serial.begin(9600);
}

void move_servo(int deg) {
  digitalWrite(pin, HIGH);
  delayMicroseconds(deg);
  digitalWrite(pin, LOW);
  delay(20);
}

int serialRead() {
  char c[4];
  for (int i = 0; i < 3; i++) {
    c[i] = Serial.read();
    if (c[i] == '\0') {
      break;
    }
  }
  return atoi(c);
}

void loop() {
  move_servo(deg);
  if (Serial.available() > 0) {
    delay(10);
    int input = serialRead();
    if ( input >= 0 && input <= 180 ) {
      deg = 610 + input / 180.0 * ( 2350 - 610 );
    }
  }
}

SG90 のサーボモータは送信するパルス幅の長さが約 610ms から 2350ms で 0 度 から 180 度の動きをするようです
そのパルスを送信する周期を 20ms にしています

送信の仕方は

digitalWrite(pin, HIGH);
delayMicroseconds(deg);
digitalWrite(pin, LOW);

で実現しており、HIGH の状態を指定ミリ秒続けることで幅の違うパルスを送信しています

あとは Serial.read で受け取った度数情報を delayMicroseconds に渡せるように変換してあげます

deg = 610 + input / 180.0 * ( 2350 - 610 );

loop メソッドはその名の通りずっとループしています
なので、move_servo は 20ms の wait をおいて常にコールされているため入力を変えない限り 20ms の間隔で同じパルス幅の信号が送信されていることになります

このプログラムで実際に動作させてみると入力した角度に動くのは動くのですが、サーボモータが動いていないときに「ジリジリ」という音と微妙な振動が発生してしまいました
おそらくですが、これは正確に PWM を送信されていないのが原因で、毎回 20ms 待つようにしていますが、Serial の読み込み等の他の処理があり周期が毎回正確になっておらず、サーボモータが不安定な状態になっているのだと思います

次に Servo ライブラリを使った制御を紹介しますが、結論から先に言うとライブラリを使った制御のほうが圧倒的に安定します
とりあえず今回は PWM の仕組みも知りたかったの、独自の制御方式も試してみた感じです

Servo ライブラリを使って PWM を送信

では、次に Arduino がデフォルトで提供している Servo ライブラリを使って制御してみたいと思います

#include <Servo.h>
Servo servo;

int pin = 8;
int deg = 0;

void setup() {
  servo.attach(pin);
  Serial.begin(9600);
}

void move_servo(int deg) {
  servo.write(deg);
  delay(200);
  servo.detach();
}

int serialRead() {
  char c[4];
  for (int i = 0; i < 3; i++) {
    c[i] = Serial.read();
    if (c[i] == '\0') {
      break;
    }
  }
  return atoi(c);
}

void loop() {
  move_servo(deg);
  if (Serial.available() > 0) {
    delay(10);
    int input = serialRead();
    if ( input >= 0 && input <= 180 ) {
      servo.attach(pin);
      deg = input;
    }
  }
}

ソースもかなり簡潔になりました
Servo ライブラリを使うと servo.write で角度の情報をそのまま渡せます
なので、パルス幅を計算するロジックがなくなります

独自のパルスを送信しているときにジリジリ言っていた問題は

delay(200);
servo.detach();

のロジックでほぼ言わなくなりました
当たり前といえば当たり前ですが、サーボモータを回したくないときは detach することで無駄な信号の送信を抑制します
再度サーボモータを回したいときに attach しています

サーボモータを servo.write で回してからdetach するまでに 200ms の delay を入れています
これは、回した直後に detach してしまうと write の情報がサーボモータに届く前にサーボモータを detach してしまうので、サーボモータが上手く回ってくれないためです

実行してみる

どちらもコードを Arduino IDE に貼り付けて配線して Run すれば OK です
動画等はなくて恐縮ですが、実行して比較してみると Servo ライブラリを使ったほうが断然いい感じにサーボモータが動きます

最後に

Arduino でサーボモータを制御してみました
PWM に詳しく実際に PWM でモータやセンサー等を制御したことある場合は独自の PWM 実装でもいいかもしれませんが、PWM について良くわからないということであれば素直に Servo ライブラリを使うことをオススメします

サーボモータは RaspberryPi でも制御できるので機会があれば RPi でも制御してみたいと思います
Arduino と RaspberryPi どちらでもできるけど、アナログ信号や PWM を扱う場合は Arduino のほうが簡単に制御できる気がします
両方使うとボードが増えて大変ですが、、

参考サイト