آلة المسار: فكرة خوارزمية واحدة

الخلفية


منذ حوالي 15 عامًا ، علمت بوجود مسارات أساسية - وهي مجموعات يمكنها تمييز المساحات الطوبولوجية عن طريق الاتصال. لن يكون البقية حولهم ، لكنهم توصلوا إلى فكرة وجود منظم وتصنيف - دون أي تحسينات بناءً على تخزين العينة.

مزيد من التفاصيل.

أفكار وتخمينات


بما أن المسارات الأساسية هي حلقات من نقطة محددة ، ومعادلة لها ، فكيف يمكن العثور على هذه الحلقات في البيانات؟ وهل من الضروري؟

جاءت الفكرة عن طريق القياس مع KNN و SVM - خوارزميات "مماثلة". ماذا لو تم استبدال الحلقة بـ "ماسورة" ، وأسطوانة من نقطة بيانات إلى أخرى ، وما تم إسقاطه في الأنبوب يتم تعيينه إلى نفس الفئة مثل هاتين النقطتين من المسار (لم تعد أساسية)؟ يجب أن يكون "الأنبوب" بعرض بحيث يصنف الفئات بشكل صحيح ... وفي الحد يكون خطًا مستقيمًا. يجب أن تكون المسافة إلى نقطة جديدة غير معروفة على طول الطريق ، ثم يمكننا أن نعزوها إلى نفس الفئة مثل المسار.



يمكن إنشاء الانحدار عن طريق إسقاط نقطة على خط مستقيم بين نقاط البيانات ، واستكمال القيم المستهدفة المقابلة لنقاط البيانات بنفس النسبة التي يقسم فيها الإسقاط المسار.

تتذكر طريقة البناء هذه العينة بالكامل وتقدم تنبؤات دقيقة حول مجموعة التدريب.

التنفيذ البدائي


كيفية بناء مسار؟ أخذت الحد الأقصى للعنصر وفقًا للمعيار ، وبدأت في البحث عن الأقرب إليه ، وربط المسار المستلم. في النهاية - مغلق مع البداية (قابل للنقاش بالطبع ، لكن هكذا).

مقدر
هذه هي النسخة الأولى من الرمز ، راجع دفتر الملاحظات المحدث أدناه.

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)) 


نظريًا (وتقنيًا) ، من الممكن جعل توقع التنبؤ بالطريقة نفسها - 1 - المسافات الطبيعية. لكني لم أتوصل إلى فرصة لاختبار الاحتمالات حقًا ، لذا ...

بعض الاختبارات


لقد بدأت مع Boston Housing و Iris الكلاسيكية ، وخط الأساس اخترت Ridge و LogisticRegression. حسنا ، ماذا ، ماذا لو.

بشكل عام ، أقترح النظر في دفتر الملاحظات jupyter .

باختصار: عمل الانحدار بشكل أسوأ ، وعمل المصنف بشكل أفضل.
تحديث: بعد StandardScaler ، عمل الانحدار بشكل أفضل.

ركبت أيضا على مجموعات البيانات الاصطناعية. بشكل عام هناك شيء في الغابة ، وهو الحطب.

لكن هذا لوحظ:

  1. يعمل الانحدار بشكل جيد في المساحات ذات الأبعاد الصغيرة ،
  2. يكون كل من الانحدار والمصنف حساسين للضوضاء ، بدءًا من عتبة معينة ،
  3. يشبه الانحدار ، على عكس الخط ، تعدد الوسائط المشتبه به (في Boston Housing) ،
  4. عن طريق البناء ، عمل المصنف بشكل جيد (الأفضل على الإطلاق ... :)) على مجموعة من الأقمار التي لا يمكن فصلها خطياً.

الاستنتاجات والإيجابيات والسلبيات والتطبيق


أنا شخصياً لا أرى أي مزايا في التنفيذ الحالي. من الناحية الفنية والرياضية. ربما يمكن تعديل هذا إلى شيء أكثر عقلانية. وفقا لذلك ، لا أرى أي انطباق معين سواء. هل من الممكن العمل مع بيانات صغيرة جدًا بدون معالجة مسبقة ...

هناك العديد من العيوب: مطلوب الاحتفاظ بمجموعة البيانات بالكامل في الذاكرة ، وقدرة التعميم مبنية على الاستقراء ، والمعلمة الرئيسية والوحيدة - المعيار - غير قابلة للتعداد الخاص - التعداد.

لكن ، على أي حال ، بالنسبة لي كان الاستلام مفاجأة ، وقررت أن أشاركه هنا.

Source: https://habr.com/ru/post/ar429974/


All Articles