Predicción del modelo cuantitativo de IA

Las finanzas cuantitativas han estado en países extranjeros durante décadas, pero han surgido en China hace menos de una década. Esta es un área muy desafiante. Las finanzas cuantitativas combinan la esencia de la estadística matemática, la teoría financiera, la sociología, la psicología y otras disciplinas, y prestan especial atención a la práctica. Debido a la diversidad de individuos involucrados en el juego de mercado y la complejidad de los efectos de grupo, las finanzas cuantitativas se caracterizan por grandes desafíos y grandes oportunidades. Esta competencia utiliza los métodos y herramientas de big data y aprendizaje automático para comprender los principios del comportamiento del mercado, crear estrategias cuantitativas a través de análisis y modelos de datos, utilizar datos históricos para verificar la efectividad de las estrategias cuantitativas y realizar evaluaciones a través de datos en tiempo real.

Conjunto de datos dado: conjunto de capacitación dado (incluido el conjunto de validación), que incluye 10 acciones (no públicas), 79 días de negociación de datos instantáneos L1 (los primeros 64 días de negociación son datos de capacitación, con fines de capacitación; los últimos 15 días de negociación son datos de prueba , que no se puede utilizar para capacitación. Los datos se han normalizado y oculto, incluidos 5 archivos de volumen/precio, precio medio, volumen de operaciones y otros datos (consulte la descripción de los datos de seguimiento para obtener más detalles).

Tarea de pronóstico: uso de datos pasados ​​y actuales para predecir la dirección de movimiento de la futura paridad central, entrenamiento de modelos y pronósticos sobre los datos.

Introducir datos:

Frecuencia del mercado: 3 segundos por punto de datos (también conocido como instantánea de 1 tick);

Cada punto de datos incluye el último precio de transacción actual/precio del quinto volumen/monto de la transacción en los últimos 3 segundos;

Cada punto de datos del conjunto de entrenamiento contiene 5 etiquetas de predicción; Le permite utilizar datos de no más de 100 ticks en el pasado (incluido el tick actual) para predecir la dirección del movimiento del punto medio después de N ticks en el futuro.

Intervalo de tiempo de predicción: 5, 10, 20, 40, 60 ticks, 5 tareas de predicción;

Es decir, después de que t+5tick, t+10tick, t+20tick, t+40tick y t+60tick se pronostiquen respectivamente en el momento t: el último precio medio se compara con el precio medio en el momento t: abajo/sin cambios/arriba.

Conjunto de datos del concurso
Frecuencia de mercado: 3 segundos por punto de datos (también conocido como instantánea de 1 tick);
Cada punto de datos incluye el último precio de transacción actual/precio del quinto volumen/monto de la transacción en los últimos 3 segundos;
Cada punto de datos del conjunto de entrenamiento contiene 5 etiquetas de predicción; Le permite utilizar datos de no más de 100 ticks en el pasado (incluido el tick actual) para predecir la dirección del movimiento del punto medio después de N ticks en el futuro.
Intervalo de tiempo de predicción: 5, 10, 20, 40, 60 ticks, 5 tareas de predicción; Es decir, después de que t+5tick, t+10tick, t+20tick, t+40tick y t+60tick se pronostiquen respectivamente en el momento t: el último precio medio se compara con el precio medio en el momento t: abajo/sin cambios/arriba.

Índice de evaluación
Según el archivo de resultados enviado, la puntuación macro-F1 se utiliza para evaluar el modelo y la puntuación más alta entre etiqueta_5, etiqueta_10, etiqueta_20, etiqueta_40 y etiqueta_60 se toma como puntuación final.

Idea de solución
La tarea es construir un modelo cuantitativo de IA que utilice datos pasados ​​y actuales para predecir la dirección de futuros movimientos del punto medio. Esta tarea de cuantificación de IA es un problema típico de predicción de regresión de series temporales. Para abordar este problema, generalmente se recomienda utilizar métodos de aprendizaje automático, como CatBoost, LightGBM, XGBoost y otros modelos de árbol, que pueden manejar mejor datos numéricos y tienen una alta interpretabilidad. O se utiliza el método de aprendizaje profundo, pero la construcción del modelo profundo es complicada y la estructura del modelo debe construirse usted mismo. Los datos numéricos deben estandarizarse y la interpretabilidad es débil. Cuando resolvemos problemas de aprendizaje automático, generalmente seguimos el siguiente proceso:

 En esta línea de base, utilizamos el modelo CatBoost de aprendizaje automático para resolver este problema y lo guiaremos a través de todo el proceso de práctica de competencia de exploración de datos, ingeniería de características, entrenamiento de modelos, validación de modelos y producción de resultados.

3.2.1 Módulo de importación

Importe los módulos necesarios para nuestro código de referencia (puede ejecutarlos todos directamente utilizando este proyecto. Si desea probarlo localmente, consulte el Apéndice:  Configuración del entorno de práctica ) .

# 解压数据集,更新所需依赖包

_ = !unzip -qo data/data233139/AI量化模型预测挑战赛公开数据.zip
_ = !pip install --upgrade catboost xgboost lightgbm
import numpy as np
import pandas as pd
from catboost import CatBoostClassifier
from sklearn.model_selection import StratifiedKFold, KFold, GroupKFold
from sklearn.metrics import accuracy_score, f1_score, roc_auc_score, log_loss, mean_squared_log_error
import tqdm, sys, os, gc, argparse, warnings
import matplotlib.pyplot as plt
warnings.filterwarnings('ignore')

3.2.2 Exploración de datos

El análisis exploratorio de datos consiste en comprender el conjunto de datos, la relación entre variables y la relación entre variables y valores predichos, lo que nos ayuda a realizar mejor la ingeniería de características y construir modelos más adelante, que es un paso muy importante en el aprendizaje automático.

# 读取数据
path = 'AI量化模型预测挑战赛公开数据/'

train_files = os.listdir(path+'train')
train_df = pd.DataFrame()
for filename in tqdm.tqdm(train_files):
    tmp = pd.read_csv(path+'train/'+filename)
    tmp['file'] = filename
    train_df = pd.concat([train_df, tmp], axis=0, ignore_index=True)

test_files = os.listdir(path+'test')
test_df = pd.DataFrame()
for filename in tqdm.tqdm(test_files):
    tmp = pd.read_csv(path+'test/'+filename)
    tmp['file'] = filename
    test_df = pd.concat([test_df, tmp], axis=0, ignore_index=True)
100%|██████████| 1225/1225 [04:26<00:00, 4.59it/s] 
100%|██████████| 296/296 [00:14<00:00, 20.00it/s]

En primer lugar, puede realizar un análisis visual de los precios de compra y venta.

Seleccione cualquier dato bursátil para un análisis visual y observe la relación entre el precio de compra y el precio de venta. La siguiente es una breve introducción a los precios de compra y venta:

  • El precio de oferta se refiere al precio más alto que un comprador está dispuesto a pagar por una acción/activo.
  • El precio de venta se refiere al precio más bajo que el vendedor está dispuesto a aceptar por una acción/activo.
  • La diferencia entre estos dos precios se llama diferencial; cuanto menor sea el diferencial, mayor será la liquidez del instrumento.

cols = ['n_bid1','n_bid2','n_ask1','n_ask2']
tmp_df = train_df[train_df['file']=='snapshot_sym7_date22_pm.csv'].reset_index(drop=True)[-500:]
tmp_df = tmp_df.reset_index(drop=True).reset_index()
for num, col in enumerate(cols):
    plt.figure(figsize=(20,5))
   
    plt.subplot(4,1,num+1)
    plt.plot(tmp_df['index'],tmp_df[col])
    plt.title(col)
plt.show()
plt.figure(figsize=(20,5))

for num, col in enumerate(cols):
    plt.plot(tmp_df['index'],tmp_df[col],label=col)
plt.legend(fontsize=12)

<Tamaño de figura 2000x500 con 1 eje>

<Tamaño de figura 2000x500 con 1 eje>

<Tamaño de figura 2000x500 con 1 eje>

<Tamaño de figura 2000x500 con 1 eje>
<matplotlib.legend.Legend en 0x7f16327b7400>

<Tamaño de figura 2000x500 con 1 eje>

Además, se sigue visualizando el precio medio, que es el promedio del precio de compra y el precio de venta, se da directamente en los datos y también podemos calcularlo nosotros mismos.

plt.figure(figsize=(20,5))

for num, col in enumerate(cols):
    
    plt.plot(tmp_df['index'],tmp_df[col],label=col)
    
plt.plot(tmp_df['index'],tmp_df['n_midprice'],label="n_midprice",lw=10)
plt.legend(fontsize=12)
<matplotlib.legend.Legend en 0x7f16305dd0c0>

<Tamaño de figura 2000x500 con 1 eje>

La volatilidad es un indicador estadístico importante del cambio de precio de una acción determinada, por lo que para calcular el cambio de precio primero debemos realizar una valoración de las acciones a intervalos regulares. Usaremos el precio promedio ponderado (WAP) de los datos proporcionados para la visualización, y los cambios en WAP reflejan la volatilidad de las acciones.

train_df['wap1'] = (train_df['n_bid1']*train_df['n_bsize1'] + train_df['n_ask1']*train_df['n_asize1'])/(train_df['n_bsize1'] + train_df['n_asize1'])
test_df['wap1'] = (test_df['n_bid1']*test_df['n_bsize1'] + test_df['n_ask1']*test_df['n_asize1'])/(test_df['n_bsize1'] + test_df['n_asize1'])

tmp_df = train_df[train_df['file']=='snapshot_sym7_date22_pm.csv'].reset_index(drop=True)[-500:]
tmp_df = tmp_df.reset_index(drop=True).reset_index()
plt.figure(figsize=(20,5))
plt.plot(tmp_df['index'], tmp_df['wap1'])
[<matplotlib.lines.Line2D en 0x7f16304bf970>]

<Tamaño de figura 2000x500 con 1 eje>

3.2.3 Ingeniería de características

En la etapa de ingeniería de características, se construyen características de tiempo básicas y se extraen características relacionadas, como horas y minutos, principalmente para caracterizar la información diferencial que puede existir en diferentes etapas de tiempo. Cabe señalar que los datos se almacenan en varios archivos, por lo que los archivos deben fusionarse antes de realizar trabajos posteriores.

# 时间相关特征
train_df['hour'] = train_df['time'].apply(lambda x:int(x.split(':')[0]))
test_df['hour'] = test_df['time'].apply(lambda x:int(x.split(':')[0]))

train_df['minute'] = train_df['time'].apply(lambda x:int(x.split(':')[1]))
test_df['minute'] = test_df['time'].apply(lambda x:int(x.split(':')[1]))

# 入模特征
cols = [f for f in test_df.columns if f not in ['uuid','time','file']]

3.2.4 Entrenamiento y verificación del modelo

La elección de utilizar el modelo CatBoost se suele utilizar como modelo de referencia en las competiciones de aprendizaje automático y puede obtener puntuaciones relativamente estables sin necesidad de ajustar los parámetros del proceso. Aquí, se utiliza un método de validación cruzada quíntuple para la verificación de la segmentación de datos, y los cinco resultados del modelo finalmente se promedian como envío final.

def cv_model(clf, train_x, train_y, test_x, clf_name, seed = 2023):
    folds = 5
    kf = KFold(n_splits=folds, shuffle=True, random_state=seed)
    oof = np.zeros([train_x.shape[0], 3])
    test_predict = np.zeros([test_x.shape[0], 3])
    cv_scores = []
    
    for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
        print('************************************ {} ************************************'.format(str(i+1)))
        trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]
       
        if clf_name == "cat":
            params = {'learning_rate': 0.2, 'depth': 6, 'bootstrap_type':'Bernoulli','random_seed':2023,
                      'od_type': 'Iter', 'od_wait': 100, 'random_seed': 11, 'allow_writing_files': False,
                      'loss_function': 'MultiClass'}
            
            model = clf(iterations=100, **params)
            model.fit(trn_x, trn_y, eval_set=(val_x, val_y),
                      metric_period=20,
                      use_best_model=True, 
                      cat_features=[],
                      verbose=1)
            
            val_pred  = model.predict_proba(val_x)
            test_pred = model.predict_proba(test_x)
        
        oof[valid_index] = val_pred
        test_predict += test_pred / kf.n_splits
        
        F1_score = f1_score(val_y, np.argmax(val_pred, axis=1), average='macro')
        cv_scores.append(F1_score)
        print(cv_scores)
        
    return oof, test_predict
    
for label in ['label_5','label_10','label_20','label_40','label_60']:
    print(f'=================== {label} ===================')
    cat_oof, cat_test = cv_model(CatBoostClassifier, train_df[cols], train_df[label], test_df[cols], 'cat')
    train_df[label] = np.argmax(cat_oof, axis=1)
    test_df[label] = np.argmax(cat_test, axis=1)

Esta competencia utiliza la puntuación macro-F1 para la evaluación. La puntuación más alta entre los cinco elementos etiqueta_5, etiqueta_10, etiqueta_20, etiqueta_40 y etiqueta_60 se toma como puntuación final. Por lo tanto, durante el modelado inicial, los cinco objetivos correspondientes deben modelarse para determinar la puntuación El objetivo más alto, al optimizar más adelante, solo necesita modelar el objetivo óptimo, lo que ahorra mucho tiempo y se centra en la optimización de un solo objetivo.

3.2.5 Salida de resultados

Los resultados enviados deben cumplir con los resultados del envío de muestra y luego comprimir la carpeta en formato zip para su envío.

test_df.head(5)
   uuid fecha hora sym n_close cantidad_delta n_midprice n_bid1 \ 
0 0 65 13:10:03 0 -0.027661 1334257.0 -0.027048 -0.027486    
1 1 65 13:10:06 0 -0.026611 194481.0 -0.0 26961 -0.027311    
2 2 65 13:10:09 0 -0,026611 122263,0 -0,026786 -0,026961    
3 3 65 13:10:12 0 -0,026786 216809,0 -0,025910 -0,026786    
4 4 65 13:10:15 0 -0,026611 4448 1.0 -0.025648 -0.026261    

       n_bsize1 n_bid2 ... n_ask5 n_asize5 \ 
0 1.250056e -05 -0,027661 ... -0,02416 0,000008    
1 5,984310e-07 -0,027486 ... -0,02416 0,000008     
2 9,641389e-07 -0,027136 ... -0,02451 0,000002   
3 1,496078e-07 -0,026961 ... -0,02416 0,000008   
4 2.161001e-07 -0.026611 ... -0.02416 0.000008    

                          archivo hora minuto label_5 label_10 label_20 \ 
0 snapshot_sym0_date65_pm.csv 13 10 1 1 1    
1 snapshot_sym0_date65_pm.csv 13 10    1 1 1 
2 snapshot_ sym0_date65_pm.csv 13 10 1 1 1    
3 instantánea_sym0_date65_pm. csv 13 10 1 1 1    
4 snapshot_sym0_date65_pm.csv 13 10 1 1 1    

   label_40 label_60   
0 1 1   
1 1 1   
2 1 1    
3 1 1  
4 1 1   

[5 filas x 35 columnas]

import pandas as pd
import os


# 指定输出文件夹路径
output_dir = './submit'

# 如果文件夹不存在则创建
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

# 首先按照'file'字段对 dataframe 进行分组
grouped = test_df.groupby('file')

# 对于每一个group进行处理
for file_name, group in grouped:
    # 选择你所需要的列
    selected_cols = group[['uuid', 'label_5', 'label_10', 'label_20', 'label_40', 'label_60']]
    
    # 将其保存为csv文件,file_name作为文件名
    selected_cols.to_csv(os.path.join(output_dir, f'{file_name}'), index=False)

# 现在就可以得到答案的压缩包啦~~~
_ = !zip -r submit.zip submit/

4. Práctica avanzada

En la etapa inicial, utilizamos CatBoost para completar todo el proceso de resolución de problemas de aprendizaje automático y obtuvimos puntuaciones básicas. En la parte de práctica avanzada, se realizarán más optimizaciones sobre la base de la línea de base original y las ideas generales de optimización se pensarán a partir de ingeniería de características y modelos.

Sugerencias de métodos de optimización :

  1. Extraiga más funciones : en las competiciones de minería de datos, las funciones son siempre el arma ganadora definitiva. Piense en qué información puede ayudarnos a mejorar la precisión de la predicción y luego conviértala en funciones e introdúzcalas en el modelo. Para esta cuestión de competencia, las características se pueden construir desde una perspectiva empresarial. En la dirección del comercio cuantitativo, los factores comúnmente mencionados son básicamente consistentes con las características del aprendizaje automático, como el factor de tendencia, el factor de volatilidad de retorno, la presión de compra y venta, etc. La atenuación del volumen de operaciones, la extensión/profundidad de la pendiente, se pueden construir en torno al volumen, los precios de oferta y demanda. Las características también se pueden construir desde la perspectiva de la predicción de series de tiempo, como características de traducción histórica, características de diferencia y características estadísticas de ventana.

  2. Pruebe diferentes modelos : existen grandes diferencias entre los modelos y los resultados de la predicción también serán diferentes. El proceso de la competencia es un proceso de experimentación continua y prueba y error. A través de experimentos continuos, encontramos el mejor modelo y al mismo tiempo ayudarnos a fortalecer nuestra comprensión del modelo .

4.1 Selección de funciones

Aquí construimos principalmente características de tiempo actual, características de traducción histórica, características diferenciales y características estadísticas de ventana; cada característica está bien fundamentada y se explica en detalle a continuación:

(1) Características del tiempo actual : construidas en torno al precio de compra y venta y el volumen de compra y venta. Por el momento, solo se construyen las características relacionadas con comprar uno, vender uno y comprar dos, vender dos. Otra información de compra y venta se puede agregar durante la optimización;

(2) Características de la traducción histórica : obtener la información de la etapa anterior a través de la traducción histórica;

(3) Características diferenciales : puede ayudar a obtener la diferencia de crecimiento en etapas adyacentes y describir el aumento o disminución de los datos. Sobre esta base, también se pueden construir cambios de proporción de datos adyacentes, diferencias de segundo orden, etc.

(4) Características estadísticas de ventana : Las estadísticas de ventana pueden construir diferentes tamaños de ventana y luego calcular la información media, máxima, mínima, mediana y varianza en función del rango de ventana, que puede reflejar los cambios en los datos en la etapa más reciente.

# 特征代码一览(无需运行这里)

# 为了保证时间顺序的一致性,故进行排序
train_df = train_df.sort_values(['file','time'])
test_df = test_df.sort_values(['file','time'])

# 当前时间特征
# 围绕买卖价格和买卖量进行构建
# 暂时只构建买一卖一和买二卖二相关特征,进行优化时可以加上其余买卖信息
train_df['wap1'] = (train_df['n_bid1']*train_df['n_bsize1'] + train_df['n_ask1']*train_df['n_asize1'])/(train_df['n_bsize1'] + train_df['n_asize1'])
test_df['wap1'] = (test_df['n_bid1']*test_df['n_bsize1'] + test_df['n_ask1']*test_df['n_asize1'])/(test_df['n_bsize1'] + test_df['n_asize1'])

train_df['wap2'] = (train_df['n_bid2']*train_df['n_bsize2'] + train_df['n_ask2']*train_df['n_asize2'])/(train_df['n_bsize2'] + train_df['n_asize2'])
test_df['wap2'] = (test_df['n_bid2']*test_df['n_bsize2'] + test_df['n_ask2']*test_df['n_asize2'])/(test_df['n_bsize2'] + test_df['n_asize2'])

train_df['wap_balance'] = abs(train_df['wap1'] - train_df['wap2'])
train_df['price_spread'] = (train_df['n_ask1'] - train_df['n_bid1']) / ((train_df['n_ask1'] + train_df['n_bid1'])/2)
train_df['bid_spread'] = train_df['n_bid1'] - train_df['n_bid2']
train_df['ask_spread'] = train_df['n_ask1'] - train_df['n_ask2']
train_df['total_volume'] = (train_df['n_asize1'] + train_df['n_asize2']) + (train_df['n_bsize1'] + train_df['n_bsize2'])
train_df['volume_imbalance'] = abs((train_df['n_asize1'] + train_df['n_asize2']) - (train_df['n_bsize1'] + train_df['n_bsize2']))

test_df['wap_balance'] = abs(test_df['wap1'] - test_df['wap2'])
test_df['price_spread'] = (test_df['n_ask1'] - test_df['n_bid1']) / ((test_df['n_ask1'] + test_df['n_bid1'])/2)
test_df['bid_spread'] = test_df['n_bid1'] - test_df['n_bid2']
test_df['ask_spread'] = test_df['n_ask1'] - test_df['n_ask2']
test_df['total_volume'] = (test_df['n_asize1'] + test_df['n_asize2']) + (test_df['n_bsize1'] + test_df['n_bsize2'])
test_df['volume_imbalance'] = abs((test_df['n_asize1'] + test_df['n_asize2']) - (test_df['n_bsize1'] + test_df['n_bsize2']))

# 历史平移
# 获取历史信息
for val in ['wap1','wap2','wap_balance','price_spread','bid_spread','ask_spread','total_volume','volume_imbalance']:
    for loc in [1,5,10,20,40,60]:
        train_df[f'file_{val}_shift{loc}'] = train_df.groupby(['file'])[val].shift(loc)
        test_df[f'file_{val}_shift{loc}'] = test_df.groupby(['file'])[val].shift(loc)
    
# 差分特征
# 获取与历史数据的增长关系
for val in ['wap1','wap2','wap_balance','price_spread','bid_spread','ask_spread','total_volume','volume_imbalance']:
    for loc in [1,5,10,20,40,60]:
        train_df[f'file_{val}_diff{loc}'] = train_df.groupby(['file'])[val].diff(loc)
        test_df[f'file_{val}_diff{loc}'] = test_df.groupby(['file'])[val].diff(loc)
    
# 窗口统计
# 获取历史信息分布变化信息
# 可以尝试更多窗口大小已经统计方式,如min、max、median等
for val in ['wap1','wap2','wap_balance','price_spread','bid_spread','ask_spread','total_volume','volume_imbalance']:
    train_df[f'file_{val}_win7_mean'] = train_df.groupby(['file'])[val].transform(lambda x: x.rolling(window=7, min_periods=3).mean())
    train_df[f'file_{val}_win7_std'] = train_df.groupby(['file'])[val].transform(lambda x: x.rolling(window=7, min_periods=3).std())
    
    test_df[f'file_{val}_win7_mean'] = test_df.groupby(['file'])[val].transform(lambda x: x.rolling(window=7, min_periods=3).mean())
    test_df[f'file_{val}_win7_std'] = test_df.groupby(['file'])[val].transform(lambda x: x.rolling(window=7, min_periods=3).std())

4.2 Fusión de modelos

Referencia del código de fusión del modelo:

Defina la función cv_model. Puede optar por utilizar los modelos lightgbm, xgboost y catboost internamente. Puede ejecutar estos tres modelos en secuencia y luego promediar los resultados de los tres modelos para la fusión.

# 无需运行这里

def cv_model(clf, train_x, train_y, test_x, clf_name, seed = 2023):
    folds = 5
    kf = KFold(n_splits=folds, shuffle=True, random_state=seed)
    oof = np.zeros([train_x.shape[0], 3])
    test_predict = np.zeros([test_x.shape[0], 3])
    cv_scores = []
    
    for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
        print('************************************ {} ************************************'.format(str(i+1)))
        trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]
        
        if clf_name == "lgb":
            train_matrix = clf.Dataset(trn_x, label=trn_y)
            valid_matrix = clf.Dataset(val_x, label=val_y)
            params = {
                'boosting_type': 'gbdt',
                'objective': 'multiclass',
                'num_class':3,
                'min_child_weight': 6,
                'num_leaves': 2 ** 6,
                'lambda_l2': 10,
                'feature_fraction': 0.8,
                'bagging_fraction': 0.8,
                'bagging_freq': 4,
                'learning_rate': 0.35,
                'seed': 2023,
                'nthread' : 16,
                'verbose' : -1,
            }
            model = clf.train(params, train_matrix, 2000, valid_sets=[train_matrix, valid_matrix],
                              categorical_feature=[], verbose_eval=1000, early_stopping_rounds=100)
            val_pred = model.predict(val_x, num_iteration=model.best_iteration)
            test_pred = model.predict(test_x, num_iteration=model.best_iteration)
        
        if clf_name == "xgb":
            xgb_params = {
              'booster': 'gbtree', 
              'objective': 'multi:softprob',
              'num_class':3,
              'max_depth': 5,
              'lambda': 10,
              'subsample': 0.7,
              'colsample_bytree': 0.7,
              'colsample_bylevel': 0.7,
              'eta': 0.35,
              'tree_method': 'hist',
              'seed': 520,
              'nthread': 16
              }
            train_matrix = clf.DMatrix(trn_x , label=trn_y)
            valid_matrix = clf.DMatrix(val_x , label=val_y)
            test_matrix = clf.DMatrix(test_x)
            
            watchlist = [(train_matrix, 'train'),(valid_matrix, 'eval')]
            
            model = clf.train(xgb_params, train_matrix, num_boost_round=2000, evals=watchlist, verbose_eval=1000, early_stopping_rounds=100)
            val_pred  = model.predict(valid_matrix)
            test_pred = model.predict(test_matrix)
            
        if clf_name == "cat":
            params = {'learning_rate': 0.35, 'depth': 5, 'bootstrap_type':'Bernoulli','random_seed':2023,
                      'od_type': 'Iter', 'od_wait': 100, 'random_seed': 11, 'allow_writing_files': False,
                      'loss_function': 'MultiClass'}
            
            model = clf(iterations=2000, **params)
            model.fit(trn_x, trn_y, eval_set=(val_x, val_y),
                      metric_period=1000,
                      use_best_model=True, 
                      cat_features=[],
                      verbose=1)
            
            val_pred  = model.predict_proba(val_x)
            test_pred = model.predict_proba(test_x)
        
        oof[valid_index] = val_pred
        test_predict += test_pred / kf.n_splits
        
        F1_score = f1_score(val_y, np.argmax(val_pred, axis=1), average='macro')
        cv_scores.append(F1_score)
        print(cv_scores)
        
    return oof, test_predict

# 参考demo,具体对照baseline实践部分调用cv_model函数
# 选择lightgbm模型
lgb_oof, lgb_test = cv_model(lgb, train_df[cols], train_df['label_5'], test_df[cols], 'lgb')
# 选择xgboost模型
xgb_oof, xgb_test = cv_model(xgb, train_df[cols], train_df['label_5'], test_df[cols], 'xgb')
# 选择catboost模型
cat_oof, cat_test = cv_model(CatBoostClassifier, train_df[cols], train_df['label_5'], test_df[cols], 'cat')

# 进行取平均融合
final_test = (lgb_test + xgb_test + cat_test) / 3

Promediar los resultados de la fusión es un método de fusión relativamente básico. Otro método de fusión clásico es el apilamiento, que es un marco de integración de modelos jerárquicos. Tomando dos capas como ejemplo, la primera capa está compuesta por múltiples alumnos base y su entrada es el conjunto de entrenamiento original. El modelo de la segunda capa utiliza la salida del alumno base de la primera capa como una característica y la agrega al conjunto de entrenamiento para reentrenamiento, para que Obtenga un modelo de apilamiento completo.

Primera capa: (análoga a la función cv_model)

  1. Divida los datos de entrenamiento en K pliegues (5 pliegues como ejemplo, seleccione cuatro de ellos como conjunto de entrenamiento y uno como conjunto de verificación cada vez);

  2. Para cada modelo RF, ET, GBDT y correspondientes a 5 resultados de predicción, estos 5 resultados se promedian; para el conjunto de validación, después de que un modelo haya sido validado de forma cruzada 5 veces, todos los datos del conjunto de validación contienen una etiqueta. Después de completar este paso: ** 5 conjuntos de validación (el número total es equivalente al conjunto de entrenamiento completo) tienen una etiqueta de predicción debajo de cada modelo. Cada fila de datos tiene un total de 4 etiquetas (4 modelos de algoritmo) y cada fila de datos en el conjunto de prueba también tiene una etiqueta de predicción. Tiene cuatro etiquetas (predichas por 4 modelos respectivamente) **

Segunda capa: (análoga a la función stack_model)

  1. Trate las cuatro etiquetas en el conjunto de entrenamiento más las etiquetas reales como cinco columnas de nuevas características como un nuevo conjunto de entrenamiento , seleccione un modelo de entrenamiento, entrene según el nuevo conjunto de entrenamiento y luego use el conjunto de prueba compuesto por las cuatro etiquetas en el conjunto de pruebas para hacer predicciones como resultado final.

Código de referencia de apilamiento:

Es importante tener en cuenta que el código proporciona un proceso para resolver problemas de dos clasificaciones. Para problemas de clasificación múltiple, puede probarlo usted mismo.

def stack_model(oof_1, oof_2, oof_3, predictions_1, predictions_2, predictions_3, y):
    '''
    输入的oof_1, oof_2, oof_3可以对应lgb_oof,xgb_oof,cat_oof
    predictions_1, predictions_2, predictions_3对应lgb_test,xgb_test,cat_test
    '''
    train_stack = pd.concat([oof_1, oof_2, oof_3], axis=1)
    test_stack = pd.concat([predictions_1, predictions_2, predictions_3], axis=1)
    
    oof = np.zeros((train_stack.shape[0],))
    predictions = np.zeros((test_stack.shape[0],))
    scores = []
    
    from sklearn.model_selection import RepeatedKFold
    folds = RepeatedKFold(n_splits=5, n_repeats=2, random_state=2021)
    
    for fold_, (trn_idx, val_idx) in enumerate(folds.split(train_stack, train_stack)): 
        print("fold n°{}".format(fold_+1))
        trn_data, trn_y = train_stack.loc[trn_idx], y[trn_idx]
        val_data, val_y = train_stack.loc[val_idx], y[val_idx]
        
        clf = Ridge(random_state=2021)
        clf.fit(trn_data, trn_y)

        oof[val_idx] = clf.predict(val_data)
        predictions += clf.predict(test_stack) / (5 * 2)
        
        score_single = roc_auc_score(val_y, oof[val_idx])
        scores.append(score_single)
        print(f'{fold_+1}/{5}', score_single)
    print('mean: ',np.mean(scores))
   
    return oof, predictions

4.3 La línea de base optimizada se puede ejecutar con un clic ~~

# 可一键运行~

_ = !unzip -qo data/data233139/AI量化模型预测挑战赛公开数据.zip
_ = !pip install --upgrade catboost xgboost lightgbm

import os
import shutil
import numpy as np
import pandas as pd
from catboost import CatBoostClassifier
from sklearn.model_selection import StratifiedKFold, KFold, GroupKFold
from sklearn.metrics import accuracy_score, f1_score, roc_auc_score, log_loss, mean_squared_log_error
import xgboost as xgb
import lightgbm as lgb
import tqdm, sys, os, gc, argparse, warnings
warnings.filterwarnings('ignore')
import paddle

if paddle.device.is_compiled_with_cuda():
    device = "GPU"
else:
    device = "CPU"


# 定义要检测的路径
path_to_check = 'AI量化模型预测挑战赛公开数据/test/'

# 检测.ipynb_checkpoints是否存在于这个路径中
for foldername in os.listdir(path_to_check):
    if foldername == '.ipynb_checkpoints':
        # 构造完整的文件夹路径
        full_path = os.path.join(path_to_check, foldername)
        
        # 删除文件夹及其所有内容
        shutil.rmtree(full_path)
        print(f'Removed .ipynb_checkpoints directory at: {full_path}')

# 读取数据
path = 'AI量化模型预测挑战赛公开数据/'

train_files = os.listdir(path+'train')
train_df = pd.DataFrame()
for filename in tqdm.tqdm(train_files):
    tmp = pd.read_csv(path+'train/'+filename)
    tmp['file'] = filename
    train_df = pd.concat([train_df, tmp], axis=0, ignore_index=True)

test_files = os.listdir(path+'test')
test_df = pd.DataFrame()
for filename in tqdm.tqdm(test_files):
    tmp = pd.read_csv(path+'test/'+filename)
    tmp['file'] = filename
    test_df = pd.concat([test_df, tmp], axis=0, ignore_index=True)


# 时间相关特征
train_df['hour'] = train_df['time'].apply(lambda x:int(x.split(':')[0]))
test_df['hour'] = test_df['time'].apply(lambda x:int(x.split(':')[0]))

train_df['minute'] = train_df['time'].apply(lambda x:int(x.split(':')[1]))
test_df['minute'] = test_df['time'].apply(lambda x:int(x.split(':')[1]))


# 为了保证时间顺序的一致性,故进行排序
train_df = train_df.sort_values(['file','time'])
test_df = test_df.sort_values(['file','time'])

# 当前时间特征
# 围绕买卖价格和买卖量进行构建
# 暂时只构建买一卖一和买二卖二相关特征,进行优化时可以加上其余买卖信息
train_df['wap1'] = (train_df['n_bid1']*train_df['n_bsize1'] + train_df['n_ask1']*train_df['n_asize1'])/(train_df['n_bsize1'] + train_df['n_asize1'])
test_df['wap1'] = (test_df['n_bid1']*test_df['n_bsize1'] + test_df['n_ask1']*test_df['n_asize1'])/(test_df['n_bsize1'] + test_df['n_asize1'])

train_df['wap2'] = (train_df['n_bid2']*train_df['n_bsize2'] + train_df['n_ask2']*train_df['n_asize2'])/(train_df['n_bsize2'] + train_df['n_asize2'])
test_df['wap2'] = (test_df['n_bid2']*test_df['n_bsize2'] + test_df['n_ask2']*test_df['n_asize2'])/(test_df['n_bsize2'] + test_df['n_asize2'])

train_df['wap_balance'] = abs(train_df['wap1'] - train_df['wap2'])
train_df['price_spread'] = (train_df['n_ask1'] - train_df['n_bid1']) / ((train_df['n_ask1'] + train_df['n_bid1'])/2)
train_df['bid_spread'] = train_df['n_bid1'] - train_df['n_bid2']
train_df['ask_spread'] = train_df['n_ask1'] - train_df['n_ask2']
train_df['total_volume'] = (train_df['n_asize1'] + train_df['n_asize2']) + (train_df['n_bsize1'] + train_df['n_bsize2'])
train_df['volume_imbalance'] = abs((train_df['n_asize1'] + train_df['n_asize2']) - (train_df['n_bsize1'] + train_df['n_bsize2']))

test_df['wap_balance'] = abs(test_df['wap1'] - test_df['wap2'])
test_df['price_spread'] = (test_df['n_ask1'] - test_df['n_bid1']) / ((test_df['n_ask1'] + test_df['n_bid1'])/2)
test_df['bid_spread'] = test_df['n_bid1'] - test_df['n_bid2']
test_df['ask_spread'] = test_df['n_ask1'] - test_df['n_ask2']
test_df['total_volume'] = (test_df['n_asize1'] + test_df['n_asize2']) + (test_df['n_bsize1'] + test_df['n_bsize2'])
test_df['volume_imbalance'] = abs((test_df['n_asize1'] + test_df['n_asize2']) - (test_df['n_bsize1'] + test_df['n_bsize2']))

# 历史平移
# 获取历史信息
for val in ['wap1','wap2','wap_balance','price_spread','bid_spread','ask_spread','total_volume','volume_imbalance']:
    for loc in [1,5,10,20,40,60]:
        train_df[f'file_{val}_shift{loc}'] = train_df.groupby(['file'])[val].shift(loc)
        test_df[f'file_{val}_shift{loc}'] = test_df.groupby(['file'])[val].shift(loc)
    
# 差分特征
# 获取与历史数据的增长关系
for val in ['wap1','wap2','wap_balance','price_spread','bid_spread','ask_spread','total_volume','volume_imbalance']:
    for loc in [1,5,10,20,40,60]:
        train_df[f'file_{val}_diff{loc}'] = train_df.groupby(['file'])[val].diff(loc)
        test_df[f'file_{val}_diff{loc}'] = test_df.groupby(['file'])[val].diff(loc)
    
# 窗口统计
# 获取历史信息分布变化信息
# 可以尝试更多窗口大小已经统计方式,如min、max、median等
for val in ['wap1','wap2','wap_balance','price_spread','bid_spread','ask_spread','total_volume','volume_imbalance']:
    train_df[f'file_{val}_win7_mean'] = train_df.groupby(['file'])[val].transform(lambda x: x.rolling(window=7, min_periods=3).mean())
    train_df[f'file_{val}_win7_std'] = train_df.groupby(['file'])[val].transform(lambda x: x.rolling(window=7, min_periods=3).std())
    
    test_df[f'file_{val}_win7_mean'] = test_df.groupby(['file'])[val].transform(lambda x: x.rolling(window=7, min_periods=3).mean())
    test_df[f'file_{val}_win7_std'] = test_df.groupby(['file'])[val].transform(lambda x: x.rolling(window=7, min_periods=3).std())

def cv_model(clf, train_x, train_y, test_x, clf_name, seed = 2023):
    folds = 5
    kf = KFold(n_splits=folds, shuffle=True, random_state=seed)
    oof = np.zeros([train_x.shape[0], 3])
    test_predict = np.zeros([test_x.shape[0], 3])
    cv_scores = []
    
    for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
        print('************************************ {} ************************************'.format(str(i+1)))
        trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]
        
        if clf_name == "lgb":
            train_matrix = clf.Dataset(trn_x, label=trn_y)
            valid_matrix = clf.Dataset(val_x, label=val_y)
            params = {
                'boosting_type': 'gbdt',
                'objective': 'multiclass',
                'num_class':3,
                'min_child_weight': 6,
                'num_leaves': 2 ** 6,
                'lambda_l2': 10,
                'feature_fraction': 0.8,
                'bagging_fraction': 0.8,
                'bagging_freq': 4,
                'learning_rate': 0.1,
                'seed': 2023,
                'nthread' : 16,
                'verbose' : -1,
            }
            model = clf.train(params, train_matrix, 200, valid_sets=[train_matrix, valid_matrix],
                              categorical_feature=[])
            val_pred = model.predict(val_x, num_iteration=model.best_iteration)
            test_pred = model.predict(test_x, num_iteration=model.best_iteration)
        
        if clf_name == "xgb":
            xgb_params = {
              'booster': 'gbtree', 
              'objective': 'multi:softprob',
              'num_class':3,
              'max_depth': 5,
              'lambda': 10,
              'subsample': 0.7,
              'colsample_bytree': 0.7,
              'colsample_bylevel': 0.7,
              'eta': 0.1,
              'tree_method': 'hist',
              'seed': 520,
              'nthread': 16,
              'tree_method': 'gpu_hist',
              }
            train_matrix = clf.DMatrix(trn_x , label=trn_y)
            valid_matrix = clf.DMatrix(val_x , label=val_y)
            test_matrix = clf.DMatrix(test_x)
            
            watchlist = [(train_matrix, 'train'),(valid_matrix, 'eval')]
            
            model = clf.train(xgb_params, train_matrix, num_boost_round=200, evals=watchlist)
            val_pred  = model.predict(valid_matrix)
            test_pred = model.predict(test_matrix)
            
        if clf_name == "cat":
            params = {'learning_rate': 0.1, 'depth': 5, 'bootstrap_type':'Bernoulli','random_seed':2023,
                      'od_type': 'Iter', 'od_wait': 100, 'random_seed': 11, 'allow_writing_files': False,
                      'loss_function': 'MultiClass', "task_type": device}
            
            model = clf(iterations=200, **params)
            model.fit(trn_x, trn_y, eval_set=(val_x, val_y),
                      metric_period=50,
                      use_best_model=True, 
                      cat_features=[],
                      verbose=1)
            
            val_pred  = model.predict_proba(val_x)
            test_pred = model.predict_proba(test_x)
        
        oof[valid_index] = val_pred
        test_predict += test_pred / kf.n_splits
        
        F1_score = f1_score(val_y, np.argmax(val_pred, axis=1), average='macro')
        cv_scores.append(F1_score)
        print(cv_scores)
        
    return oof, test_predict
    
# 处理train_x和test_x中的NaN值
train_df = train_df.fillna(0)
test_df = test_df.fillna(0)

# 处理train_x和test_x中的Inf值
train_df = train_df.replace([np.inf, -np.inf], 0)
test_df = test_df.replace([np.inf, -np.inf], 0)

# 入模特征
cols = [f for f in test_df.columns if f not in ['uuid','time','file']]
for label in ['label_5','label_10','label_20','label_40','label_60']:
    print(f'=================== {label} ===================')
    # 选择lightgbm模型
    lgb_oof, lgb_test = cv_model(lgb, train_df[cols], train_df[label], test_df[cols], 'lgb')
    # 选择xgboost模型
    xgb_oof, xgb_test = cv_model(xgb, train_df[cols], train_df[label], test_df[cols], 'xgb')
    # 选择catboost模型
    cat_oof, cat_test = cv_model(CatBoostClassifier, train_df[cols], train_df[label], test_df[cols], 'cat')

    # 进行取平均融合
    final_test = (lgb_test + xgb_test + cat_test) / 3

    test_df[label] = np.argmax(final_test, axis=1)


import pandas as pd
import os

# 检查并删除'submit'文件夹
if os.path.exists('./submit'):
    shutil.rmtree('./submit')
    print("Removed the 'submit' directory.")

# 检查并删除'submit.zip'文件
if os.path.isfile('./submit.zip'):
    os.remove('./submit.zip')
    print("Removed the 'submit.zip' file.")

# 指定输出文件夹路径
output_dir = './submit'

# 如果文件夹不存在则创建
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

# 首先按照'file'字段对 dataframe 进行分组
grouped = test_df.groupby('file')

# 对于每一个group进行处理
for file_name, group in grouped:
    # 选择你所需要的列
    selected_cols = group[['uuid', 'label_5', 'label_10', 'label_20', 'label_40', 'label_60']]
    
    # 将其保存为csv文件,file_name作为文件名
    selected_cols.to_csv(os.path.join(output_dir, f'{file_name}'), index=False)


_ = !zip -r submit.zip submit/

En este punto, es posible que se pregunte por qué el código sin optimización es mayor que el código después de la optimización. Esto se debe a que a medida que aumenta el número de funciones, el tiempo requerido para el entrenamiento obviamente debería ser mayor. Sin embargo, aquí para que todos puedan ejecutarlo. velocidad, simplemente configurarla iterations=100generalmente no es suficiente. Si desea lograr mejores resultados, puede considerar configurarla más alta.

 

 

Supongo que te gusta

Origin blog.csdn.net/m0_72572822/article/details/132176870
Recomendado
Clasificación