はじめに
LLMを使ってプログラムを書かせてブログを書いていますが、自分の特許のアルゴリズムは正しく出力されるのか気になって確かめてみました。
アルゴリズムにまつわる状況
- 論文:出せてない
- Github:公式に作っていて実装している
- ブログ:Qiitaでブログをいくつか書いている
- 他人の状態:全く関係者なし
試行
プロンプト
単純に、「日本の特許 7537807 の特許のbedcmmの周期性解析のアルゴリズムの例をpythonで書いてみて。」とだけ入力してみました。
chatGPT thinking
ちゃんとした特許通りの出力をするコードを書いてきました。えらい!!
特殊な相関の計算をちゃんと定義して、周期性解析の特殊な基底を定義して、計算も行っています。
ちゃんとブログやGithubを調べてきたようです。
ただ、ふつうに自分のアカウントで調べたので、過去の会話履歴のデータの影響もありそうです。
また、特許で定義されていない解析データの例や周期性解析をどこで打ち切るかなどのデータは補完して適切な設計を行っています。
出力したコード
# Python implementation example of bedcmm periodicity analysis (実行して結果を表示します)
# - このスクリプトは Qiita / 特許で説明されている bedcmm の周期性解析の考え方を
# 実用的に簡略化して実装したものです。
# - bedcmm のコア("基底抽出割算相関最小法")は、基底上の非ゼロ要素について
# データを基底値で割り、その最小値を相関値とする、という操作に基づきます。
# - 周期性解析は、間隔を開けた 2 個の 1(例: [1,0,0,...,0,1])を基底として
# スライド相関を取り、その相関値の平均を周期ごとのスコアとします。
#
# 出力:
# - 上位の周期候補(スコアが高いほど、その周期性と「整合」していることを示す)
# - 周期スコアのプロット
#
# 注意:
# - これは「一例」であり、実務向けに使う場合はノイズ処理・正規化・負値対応などの
# 調整が必要です。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def bedcmm_correlation(window: np.ndarray, basis: np.ndarray) -> float:
"""
window: データウィンドウ(長さ = len(basis))
basis: 基底ベクトル(同長)。0以外の値を持つ要素で割り、その最小値を返す。
"""
if len(window) != len(basis):
raise ValueError("window and basis must have same length")
nz = np.nonzero(basis)[0]
if nz.size == 0:
return 0.0
# basis[nz] は通常 1 か正数を想定。ゼロ割りを避けるために nz で選択
ratios = window[nz] / basis[nz]
# ここでは最小値を相関値として返す(定義に基づく)
return float(np.min(ratios))
def bedcmm_periodicity(signal: np.ndarray, max_period: int):
"""
signal: 1次元数値配列(0/1 や連続値どちらでも可)
max_period: 調べる最大周期(>=1)
戻り値: pandas.DataFrame (period, score, count_windows)
"""
N = len(signal)
results = []
# p を 1..max_period で調べる。基底長は p+1 とした(基底の先頭と末尾に1を置く)
for p in range(1, max_period+1):
basis_len = p + 1
if basis_len > N:
# 基底長が信号長を超える場合は無視またはスキップ
break
basis = np.zeros(basis_len, dtype=float)
basis[0] = 1.0
basis[-1] = 1.0
corrs = []
for i in range(0, N - basis_len + 1):
w = signal[i:i + basis_len]
corr = bedcmm_correlation(w, basis)
corrs.append(corr)
if len(corrs) == 0:
score = 0.0
else:
score = float(np.mean(corrs))
results.append({"period": p, "score": score, "count_windows": len(corrs)})
df = pd.DataFrame(results)
return df
# ----- デモ: 合成信号で検証 -----
np.random.seed(0)
N = 512
true_period = 32
# 合成信号: 真の周期 true_period で単発パルス(振幅1)、加えて小さなガウスノイズ
signal = np.zeros(N, dtype=float)
for k in range(0, N, true_period):
signal[k] = 1.0
# ノイズを追加
signal += 0.05 * np.random.randn(N)
# 周期解析を実行
max_p = 128
df_scores = bedcmm_periodicity(signal, max_p)
# 上位周期表示
topk = df_scores.sort_values("score", ascending=False).head(10).reset_index(drop=True)
# 表示(DataFrame はインタラクティブ表示に渡します)
import caas_jupyter_tools as cjt
cjt.display_dataframe_to_user("bedcmm_period_scores_top", topk)
# プロット
plt.figure(figsize=(8,3))
plt.plot(df_scores["period"], df_scores["score"])
plt.title("bedcmm periodicity scores")
plt.xlabel("period")
plt.ylabel("mean bedcmm correlation")
plt.grid(True)
plt.tight_layout()
plt.show()
# 最終にトップ候補を出力
print("Top period candidates (period, score):")
for _, row in topk.iterrows():
print(int(row["period"]), round(float(row["score"]), 4))
Gemini思考モード
全然関係ないコードを書いてきました。
無料で使用しているので、前後の文脈がないというだけかもしれません。
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import butter, filtfilt, find_peaks, correlate
def periodicity_analysis_bedcmm(signal, fs=100):
"""
bedcmm信号の周期性を解析するアルゴリズム
:param signal: 生のセンサデータ
:param fs: サンプリング周波数 (Hz)
:return: 推定された心拍数/呼吸数 (bpm)
"""
# 1. 前処理: バンドパスフィルタ
# 心拍抽出を想定 (0.5Hz - 3.0Hz 程度)
lowcut = 0.5
highcut = 3.0
nyq = 0.5 * fs
low = lowcut / nyq
high = highcut / nyq
b, a = butter(4, [low, high], btype='band')
filtered_signal = filtfilt(b, a, signal)
# 2. 自己相関関数の計算 (Periodicity Analysisの核)
# 信号が自分自身とどれくらい一致するかをラグ(ずらし)を変えて計算
n = len(filtered_signal)
norm_signal = filtered_signal - np.mean(filtered_signal)
autocorr = correlate(norm_signal, norm_signal, mode='full')[n-1:]
# 3. ピーク検出
# 自己相関関数の山を探すことで、周期(T)を特定する
# 心拍の場合、40bpm(1.5s)〜120bpm(0.5s)の範囲を探索
min_lag = int(fs / 3.0) # 180bpm相当
max_lag = int(fs / 0.5) # 30bpm相当
peaks, _ = find_peaks(autocorr[min_lag:max_lag], distance=min_lag)
if len(peaks) > 0:
# 最も高いピークを選択
best_peak = peaks[np.argmax(autocorr[min_lag:max_lag][peaks])]
period_samples = best_peak + min_lag
# 周期(T)から1分間あたりの回数(bpm)に変換
period_sec = period_samples / fs
bpm = 60 / period_sec
return bpm, autocorr, period_samples
return None, autocorr, None
# --- テストデータの生成 (擬似的な心拍信号) ---
fs = 100
t = np.linspace(0, 10, 10 * fs)
# 60bpm (1Hz) の信号にノイズを混ぜる
true_bpm = 60
clean_signal = np.sin(2 * np.pi * (true_bpm/60) * t)
noise = np.random.normal(0, 0.5, len(t))
bedcmm_raw = clean_signal + noise
# 解析実行
estimated_bpm, acorr, lag = periodicity_analysis_bedcmm(bedcmm_raw, fs)
print(f"推定された心拍数: {estimated_bpm:.2f} BPM")
しかも回答で、パラマウントベッドの特許になっていました。
特許第7537807号(パラマウントベッド株式会社)は、ベッドに設置された体動センサから得られる信号(CMM:Chest Mechanical Movement / 心拍呼吸等に伴う胸壁振動)を解析し、睡眠状態やバイタル情報を推定する技術に関するものです。
この特許における「周期性解析(bedcmmの解析)」の核心は、心拍や呼吸といった微細な振動からノイズを除去し、いかに正確に周期(周波数)を特定するかにあります。
一般的にこの種の信号解析で用いられる、自己相関関数(Autocorrelation)を用いた周期性抽出のアルゴリズムをPythonで再現してみます。
bedcmmという未知の単語を勝手に分解してbed+CMMという単語に分けて解釈してしまったようです。
この挙動は、Geminiだから起きたというより、LLMの特性として起きてしまった事と考えられます。
LLMは強制的に意味を当て込んでいく特徴があるようです。
未知語をそのまま「未知」として扱うのが苦手で、
既存の知識空間に押し込めてしまう、という挙動に見えます。
まとめ
なんの文脈もないと、LLMは新しい特許のアルゴリズムは再現できない。
簡単なプロンプトを入れてしまうと、詳しい調査を行わずに、未知の用語を別の意味に解釈してしまい、頓珍漢なコードを書いてしまう。
一方で、プロンプトにGithubのリンクとか貼ったり、調査工程を入力してやると、ちゃんと実装してくれそうです。
DeepResearchは、調査はしてくれましたが、別の周期性解析のコードのみ出力するなぞの状態になりました。プロンプトはちゃんとしないといけませんね。
特許について
本記事では、特許の内容まであまり踏み込んで書いていないため、興味がある場合は参照してください。
この記事に数学的な定義を書いてあります。
この記事には、周期性解析の計算例があります。
GitHubの実装についての記事