Машинное обучение в трейдинге: теория, модели, практика и алготорговля - страница 3694

Maxim Dmitrievsky #:
Только в качестве данных используются различные тексты. Потом они обучаются предсказывать следующее слово. Принцип тот же самый, ничего опасного пока там нет :)

Это все ерунда, я с детства в ссср, думал, коггда нейросеть творить сможет и обрести желания и способность мыслить, и уже тогда боялся, простите, за трусость. :)

Почему пишу об этом, сейчас уже Алиса очень умная.

[Удален]
prosvetlenniy_mudrec #:

Это все ерунда, я с детства в ссср, думал, коггда нейросеть творить сможет и обрести желания и способность мыслить, и уже тогда боялся, простите, за трусость. :)

Почему пишу об этом, сейчас уже Алиса очень умная.

Ну да, как и калькулятор. Просто не обычно, когда программа пишет на твоём языке. Новое. 

Вы правы, необычно. Что будет когда она свой язык сделает. Ведь, мы все понимаем, что это произойдет. Здесь есть разные мнния.

Наверняка Алиса пообщется с chat gpt. Очень интересен этот разговор :)

[Удален]

Остался Claude 3.7. В програмимировании привычно сильная модель. К сожалению, Gemini и Claude изначально предложили поставить пакет nonconformist, который у меня на маке не заводится. Пришлось попросить переписать на доступные варианты. 

Дальше/позже протестирую варианты от Гемини и Клода и сравню со своим. Ну а чатГПТ в пролете, хотя я потом изучу тот пакет, который он предложил, но не смог запустить.

GitHub - donlnz/nonconformist: Python implementation of the conformal prediction framework.
GitHub - donlnz/nonconformist: Python implementation of the conformal prediction framework.
  • donlnz
  • github.com
Python implementation of the conformal prediction framework [1]. Primarily to be used as an extension to the scikit-learn library. (API documentation is currently severely deprecated; for instructions on basic usage, please refer to README.ipynb, and the running examples available under /examples/ in the repository.) nonconformist requires: The...
[Удален]

Ох жесть, нет никакой возможности разбираться в сгенеренном. Понавствалял ненужного, я его вычистил. Теперь будем просить описание :)

def meta_learners_manual_conformal_analogue(models_number: int,
                                            iterations: int,
                                            depth: int,
                                            bad_samples_fraction: float,
                                            epsilon_threshold: float = 0.1):
    """
    Модифицирует функцию meta_learners для выявления потенциально некорректных меток
    без использования библиотеки nonconformist, эмулируя идею конформных предсказаний.
    """
    global hyper_params # Предполагаем, что hyper_params - глобальный словарь, или передайте его как аргумент
    
    # Проверка на существование hyper_params, если он не определен, можно задать значения по умолчанию или вывести ошибку
    if 'hyper_params' not in globals():
        # Эта часть может быть удалена, если hyper_params всегда определен глобально
        print("Warning: 'hyper_params' not defined globally. Using mock hyper_params for structure demonstration.")
        hyper_params = {
            'markup': 0.001, 'min': 1, 'max': 15, 'direction': 1,
            'forward': pd.Timestamp.now() + pd.Timedelta(days=1),
            'backward': pd.Timestamp.now() - pd.Timedelta(days=30*12)
        }
        # Если функции get_labels_one_direction и др. не могут работать без реального hyper_params,
        # то здесь лучше выбрасывать исключение:
        # raise NameError("Global variable 'hyper_params' is not defined.")

    # Используем ваш оригинальный способ получения данных
    dataset = get_labels_one_direction(get_features(get_prices()),
                                       markup=hyper_params['markup'],
                                       min=1, # Используйте min_val, если 'min' - ключевое слово в функции
                                       max=15, # Используйте max_val, если 'max' - ключевое слово в функции
                                       direction=hyper_params['direction'])
    
    data = dataset[(dataset.index < hyper_params['forward']) & (dataset.index > hyper_params['backward'])].copy()

    if data.empty:
        print("Предупреждение: DataFrame 'data' пуст после начальной фильтрации. Проверьте hyper_params['forward'] / ['backward'] и индексы датасета.")
        data['meta_labels'] = np.nan
        return data

    # Определение имен колонок с признаками.
    # Исходный код: X = data[data.columns[1:-2]]
    # Это значит, что первая колонка пропускается (возможно, ID), и последние две тоже (метки и др.)
    if data.shape[1] < 4: # Как минимум (ID/пропускаемая, признак, dummy_end_col, метка) для среза [1:-2]
        raise ValueError("Недостаточно колонок в 'data' для извлечения признаков по правилу data.columns[1:-2]. "
                         f"Количество колонок: {data.shape[1]}, колонки: {data.columns.tolist()}")
    feature_column_names = data.columns[1:-2].tolist()
    if not feature_column_names:
         raise ValueError(f"Список признаков пуст после среза data.columns[1:-2]. Проверьте структуру данных. Колонки: {data.columns.tolist()}")

    BAD_WAIT = pd.Index([])
    BAD_TRADE = pd.Index([])

    for i in range(models_number):
        print(f"Обработка модели {i+1}/{models_number}...")
        
        sample_frac = 0.3
        # Убедимся, что достаточно данных для выборки и последующих разделений
        if len(data) * sample_frac < 20 : # Минимальное количество для разделения на 3 части + стратификация
            print(f"Предупреждение: Общий размер данных ({len(data)}) слишком мал для выборки {sample_frac*100}% и последующих разделений. "
                  "Пропуск всех итераций.")
            break 
            
        current_sample_df = data.sample(frac=sample_frac, random_state=i)
        
        X_current_sample = current_sample_df[feature_column_names]
        y_current_sample = current_sample_df['labels']

        if len(X_current_sample) < 6: # Мин. для 3-х сплитов, по 2 на каждый для стратификации
            print(f"Пропуск итерации {i+1} из-за недостаточного количества образцов ({len(X_current_sample)}) в current_sample_df для разделения.")
            continue
        
        stratify_opt_y_current = y_current_sample if y_current_sample.nunique() > 1 else None
        
        try:
            # Разделение current_sample_df на: 1. Обучение (Proper Train), 2. Калибровка (Calibration), 3. Валидация CatBoost (Eval Set)
            X_proper_train_temp, X_cb_eval, y_proper_train_temp, y_cb_eval = train_test_split(
                X_current_sample, y_current_sample, test_size=0.2, shuffle=True, 
                stratify=stratify_opt_y_current, random_state=i
            )

            if len(X_proper_train_temp) < 2: # Нужно хотя бы 2 для следующего разделения
                 raise ValueError("Недостаточно данных после первого разделения для дальнейшего деления на обучение и калибровку.")

            stratify_opt_y_proper_temp = y_proper_train_temp if y_proper_train_temp.nunique() > 1 else None
            X_prop_train, X_calib, y_prop_train, y_calib = train_test_split(
                X_proper_train_temp, y_proper_train_temp, test_size=0.25, # 0.25 * 0.8 = 0.2 (20% от current_sample_df для калибровки)
                shuffle=True, stratify=stratify_opt_y_proper_temp, random_state=i
            )
        except ValueError as e: # Перехват ошибок от train_test_split (например, недостаточно членов в классе для стратификации)
            print(f"Ошибка при разделении данных в итерации {i+1}: {e}. Пропуск итерации.")
            continue
            
        if X_prop_train.empty or X_calib.empty or X_cb_eval.empty:
            print(f"Пропуск итерации {i+1} из-за того, что одно из разделений (обучение, калибровка или валидация CB) оказалось пустым.")
            continue

        meta_m = CatBoostClassifier(iterations=iterations,
                                    depth=depth,
                                    eval_metric='Accuracy', # В вашем оригинале было custom_loss=['Accuracy'], eval_metric='Accuracy'
                                    verbose=False,
                                    use_best_model=True,
                                    random_seed=i,
                                    early_stopping_rounds=10 if iterations > 20 else None)
        
        meta_m.fit(X_prop_train, y_prop_train, eval_set=(X_cb_eval, y_cb_eval), plot=False)
        
        # --- Начало ручной реализации аналога конформного подхода ---

        # 1. Получить предсказанные вероятности для калибровочного набора
        calib_probs = meta_m.predict_proba(X_calib)
        
        # 2. Вычислить "оценки неконформности" для калибровочного набора
        #    Оценка = 1 - P(истинная_метка)
        calib_scores = np.zeros(len(y_calib))
        # Используем y_calib.values или y_calib.to_numpy() для numpy операций, если y_calib - это Series
        y_calib_numpy = y_calib.to_numpy() if isinstance(y_calib, pd.Series) else y_calib

        for k_idx, true_label_val in enumerate(y_calib_numpy):
            true_label_idx = int(true_label_val)
            if 0 <= true_label_idx < calib_probs.shape[1]:
                calib_scores[k_idx] = 1.0 - calib_probs[k_idx, true_label_idx]
            else:
                calib_scores[k_idx] = 1.0 # Максимальная неконформность

        if len(calib_scores) == 0:
            print(f"Предупреждение: калибровочный набор пуст или не удалось вычислить оценки в итерации {i+1}. Пропуск этой итерации.")
            continue

        # 3. Предсказание для current_sample_df (coreset) и вычисление "эмпирических p-значений"
        X_coreset_features = current_sample_df[feature_column_names]
        y_coreset_true_labels = current_sample_df['labels']
        
        coreset_probs = meta_m.predict_proba(X_coreset_features)
        
        calculated_p_values = np.zeros(len(y_coreset_true_labels))
        y_coreset_true_labels_numpy = y_coreset_true_labels.to_numpy() if isinstance(y_coreset_true_labels, pd.Series) else y_coreset_true_labels

        for k in range(len(y_coreset_true_labels_numpy)):
            true_label_class_idx = int(y_coreset_true_labels_numpy[k])
            
            current_score = 1.0 
            if 0 <= true_label_class_idx < coreset_probs.shape[1]:
                current_score = 1.0 - coreset_probs[k, true_label_class_idx]
            else: # Если метка вне диапазона (маловероятно для 0/1), считаем максимально неконформной
                 pass # current_score уже 1.0

            p_val = (np.sum(calib_scores >= current_score) + 1.0) / (len(calib_scores) + 1.0)
            calculated_p_values[k] = p_val

        # --- Конец ручной реализации ---
        
        suspicious_mask = calculated_p_values < epsilon_threshold
        suspicious_original_indices = current_sample_df.index[suspicious_mask]
        
        flagged_bad_samples_df = current_sample_df.loc[suspicious_original_indices]
        
        diff_negatives_w_indices = flagged_bad_samples_df[flagged_bad_samples_df['labels'] == 0].index
        diff_negatives_t_indices = flagged_bad_samples_df[flagged_bad_samples_df['labels'] == 1].index
        
        idx_type_cast = pd.DatetimeIndex if isinstance(data.index, pd.DatetimeIndex) else pd.Index
        BAD_WAIT = BAD_WAIT.union(idx_type_cast(diff_negatives_w_indices))
        BAD_TRADE = BAD_TRADE.union(idx_type_cast(diff_negatives_t_indices))

    # Инициализируем to_mark_w и to_mark_t пустыми Series, если BAD_WAIT/BAD_TRADE пустые
    # чтобы избежать ошибок при вызове .mean() или доступе по индексу
    to_mark_w = pd.Series(BAD_WAIT.value_counts()) if not BAD_WAIT.empty else pd.Series(dtype='int64')
    to_mark_t = pd.Series(BAD_TRADE.value_counts()) if not BAD_TRADE.empty else pd.Series(dtype='int64')

    threshold_w = (to_mark_w.mean() * bad_samples_fraction) if not to_mark_w.empty else 0
    threshold_t = (to_mark_t.mean() * bad_samples_fraction) if not to_mark_t.empty else 0
    
    marked_idx_w = to_mark_w[to_mark_w > threshold_w].index if not to_mark_w.empty else pd.Index([])
    marked_idx_t = to_mark_t[to_mark_t > threshold_t].index if not to_mark_t.empty else pd.Index([])

    data['meta_labels'] = 1.0
    data.loc[data.index.isin(marked_idx_w), 'meta_labels'] = 0.0
    data.loc[data.index.isin(marked_idx_t), 'meta_labels'] = 0.0
    
    return data
[Удален]

И основные отличия сгенеренного кода от изначального:

Если совсем коротко, то вместо сравнения напрямую исходных меток с предсказанными и определения ошибок предсказаний, используются p_values через предварительную калибровку.


[Удален]

Если еще проще, то он просто взял вместо меток классов их вероятности, для определения "надежности" предсказаний. То есть мой код уже реализует частично логику конформных предсказаний, но без вероятностей.

Сейчас сравню результаты.

[Удален]

Мой код:

Его код:

Слишком слабое улучшение, часто работает хуже изначального.

Опять ИИ не сделал мою жизнь лучше :) хотя в комментариях продолжает утверждать, что второй подход самый state-of-the-art'овский и лучше ещё не придумано 

Но по качеству кодинга Gemini 2.5 сейчас точно лучше ЧатГПТшных моделей и где-то на уровне Клода или выше. Наверное это самый правильный и полезный вывод из всего произошедшего :) 
Maxim Dmitrievsky #:

Мой код:

Его код:

Слишком слабое улучшение, часто работает хуже изначального.

Опять ИИ не сделал мою жизнь лучше :) хотя в комментариях продолжает утверждать, что второй подход самый state-of-the-art'овский и лучше ещё не придумано 

Но по качеству кодинга Gemini 2.5 сейчас точно лучше ЧатГПТшных моделей и где-то на уровне Клода или выше. Наверное это самый правильный и полезный вывод из всего произошедшего :) 

Пользую китайца - deepseek. Очень часто радует, но не пытался что-то свое улучшать.  Может попробуете и выложите результат?

ПС. Кстати китаец намного превосходит алису и чатгпт.

[Удален]
СанСаныч Фоменко #:

Пользую китайца - deepseek. Очень часто радует, но не пытался что-то свое улучшать.  Может попробуете и выложите результат?

ПС. Кстати китаец намного превосходит алису и чатгпт.

Китаец где-то на уровне между чатгпт и Клодом/Гемини. Ну либо на уровне chatgpt. Иногда заставляю его пошевелить транзисторами. Именно Claude и Gemini сейчас демонстрируют более продвинутые ответы в программировании , на мой взгляд.