パスマシン:1つのアルゴリズムのアイデア

背景



約15年前、私は基本的なパスの存在について学びました-接続によってトポロジ空間を区別できるグループ。 残りはそれらについてではありませんが、リグレッサーと分類子のアイデアを思い付きました-サンプルの保存に基づく最適化なし。



詳細。



アイデアと推測



基本的なパスは、選択されたポイントからのループであり、それらの同等性であるため、そのようなループはデータでどのように見つけることができますか? そしてそれは必要ですか?



このアイデアは、KNNおよびSVM(「アナログ」アルゴリズム)との類推から生まれました。 ループが「パイプ」、ある特定のポイントから別のポイントへのシリンダーに置き換えられ、パイプに落ちたものがパスのこれらの2つのポイントと同じクラスに割り当てられた場合はどうなりますか? 「パイプ」は、クラスを正しく分類できる幅である必要があります...そして、限界では直線です。 新たな未知のポイントまでの距離は、道に沿って最小でなければなりません。それから、道と同じクラスに帰することができます。







リグレッサーは、データポイント間の直線上のポイントを投影し、投影がパスを分割するのと同じ比率でデータポイントに対応するターゲット値を補間することによって構築できます。



この構築方法は、サンプル全体を記憶し、トレーニングセットの正確な予測を提供します。



プリミティブ実装



パスを作成する方法は? 基準に従って最大の要素を取得し、受信したパスを接続して、それに最も近い要素を検索し始めました。 最後に-始まりで閉じました(もちろん議論の余地がありますが、そのように)。



推定量
これはコードの最初のバージョンです。更新されたノートブックを参照してください。



class PathMachine(BaseEstimator): def __init__(self, norm=np.linalg.norm, classify=False): self.norm = norm self.classify = classify def find_start(self, X): index_max = None value_max = -np.inf for index, x in enumerate(X): value = self.norm(x) if value > value_max: index_max = index value_max = value return index_max def find_next(self, point, target, X, y): index_min = None value_min = np.inf for index, x in enumerate(X): if self.classify and (y[index] != target): continue value = self.norm(x - point) if value < value_min: index_min = index value_min = value return index_min def fit(self, X, y): X = np.copy(X) y = np.copy(y).flatten() self.paths = {} if self.classify else [] start_index = self.find_start(X) start_value = X[start_index] start_target = y[start_index] X = np.delete(X, start_index, axis=0) y = np.delete(y, start_index, axis=0) while len(X) > 0: next_index = self.find_next(start_value, start_target, X, y) if self.classify and next_index is None: start_index = self.find_start(X) start_value = X[start_index] start_target = y[start_index] continue next_target = y[next_index] if self.classify: if not next_target in self.paths: self.paths[next_target] = [] self.paths[next_target].append({ 'start': start_value, 'next': X[next_index] }) else: self.paths.append({ 'start': start_value, 'next': X[next_index], 'value': start_target, 'target': next_target }) start_value = X[next_index] start_target = y[next_index] X = np.delete(X, next_index, axis=0) y = np.delete(y, next_index, axis=0) if self.classify: self.paths[start_target].append({ 'start': start_value, 'next': self.paths[start_target][0]['start'] }) else: self.paths.append({ 'start': start_value, 'next': self.paths[0]['start'], 'value': start_target, 'target': self.paths[0]['target'] }) return self def predict(self, X): result = [] for x in X: if self.classify: predicted = None min_distance = np.inf for target in self.paths: for path in self.paths[target]: point = x - path['start'] line = path['next'] - path['start'] if np.allclose(self.norm(line), 0): continue direction = line / self.norm(line) product = np.dot(point, direction) projection = product * direction distance = self.norm(projection - point) if distance < min_distance: predicted = target min_distance = distance result.append(predicted) else: predicted = None min_distance = np.inf for path in self.paths: point = x - path['start'] line = path['next'] - path['start'] if np.allclose(self.norm(line), 0): continue direction = line / self.norm(line) product = np.dot(point, direction) projection = product * direction parameter = np.sign(product) * self.norm(projection) /\ self.norm(line) distance = self.norm(projection - point) if distance < min_distance: predicted = (1 - parameter) * path['value'] +\ parameter * path['target'] min_distance = distance result.append(predicted) return np.array(result) def score(self, X, y): if self.classify: return f1_score(y.flatten(), self.predict(X), average='micro') else: return r2_score(y.flatten(), self.predict(X))
      
      







理論的に(および技術的に)、predict_proba-1-正規化距離と同じことを行うことができます。 しかし、私は実際に確率をテストする機会を思いついていなかったので...



いくつかのテスト



クラシックなボストンハウジングとアイリスから始め、ベースラインとしてリッジとロジスティック回帰を選択しました。 まあ、何、どうすれば。



一般に、 jupyter Notebookを見ることをお勧めします。



要するに、リグレッサーの動作は悪化し、分類子の動作は向上しました。

更新:StandardScalerの後、リグレッサーの動作が改善されました。



合成データセットにも乗りました。 一般に、森には何か、つまりfireがあります。



しかし、これは気づかれました:



  1. リグレッサーは小さな次元の空間でうまく機能し、
  2. リグレッサーと分類器の両方は、特定のしきい値から始まるノイズに敏感です。
  3. リグレッサーは、ラインとは異なり、マルチモダリティを疑っていました(ボストンハウジング)。
  4. 建設によって、分類器は線形に不可分な月のセットでうまく機能しました(何よりも... :))。


結論、長所、短所、適用性



個人的には、現在の実装には利点がありません。 技術的にも数学的にも。 おそらく、これはより賢明なものに変更できます。 したがって、特定の適用可能性も見当たりません。 前処理をせずに非常に小さなデータで作業することは可能ですか?



多くの欠点があります:データセット全体をメモリに保持する必要があり、一般化機能は外挿に基づいて構築され、メインで唯一のハイパーパラメーター-ノルム-は特別な選択列挙には適していません。



しかし、とにかく、受け取ったのは驚きだったので、ここで共有することにしました。



All Articles