Discussion de l'article "Comment utiliser les modèles ONNX dans MQL5" - page 6

 
Erreur lors de l'utilisation d'un modèle-classifieur.

La régression fonctionne avec tout, la sortie est un nombre.

Mais lorsque vous demandez à un chat d'écrire un MLP-classifieur, l'Expert Advisor ne peut pas reconnaître les données de sortie de ce modèle : "Buy", "Sell", "Hold". Soit "1", "2", "3", soit "0", "1", "2".

L'erreur s'envole

2025.02.12 08:13:46.866 Core 01 2021.01.01 00:00 Erreur de définition de la forme de sortie : 5808
2025.02.12 08:13:46.866 Core 01 2021.01.01 00:00 ONNX : invalid handle passed to OnnxRelease function, inspect code 'X È$Zë3E' (291:7)

Aucun des chats, pas même Dipsic, ne comprend ou ne sait comment résoudre le problème, en générant des codes possibles qui mènent aussi à cette erreur.

Tous les chats disent la même chose : puisque c'est un classificateur MLP, il n'a que 3 sorties, selon vos étiquettes (je lui donne un fichier csv, où la dernière colonne est l'une des trois étiquettes d'une classification simple : buy, sell, hold. J'ai essayé des valeurs numériques et des chaînes de caractères dans cette colonne).

Puis ce bloc

.
const long output_shape[] = {1,1};
   if(!OnnxSetOutputShape(ExtHandle,0,output_shape))
     {
      Print("OnnxSetOutputShape error ",GetLastError());
      return(INIT_FAILED);
     }
Il modifie l'initialisation du tableau

.
const long output_shape[] = {1,3};
   if(!OnnxSetOutputShape(ExtHandle,0,output_shape))
     {
      Print("OnnxSetOutputShape error ",GetLastError());
      return(INIT_FAILED);
     }


Et une erreur apparaît.

J'essaie d'imprimer.

Print(OnnxGetOutputCount(ExtHandle));

J'obtiens 2.

Je ne comprends rien.



Si quelqu'un comprend ce qu'est l'erreur, merci de me le faire savoir.

Code Python pour le classificateur - n'importe lequel, ils génèrent tous la même erreur.

Par exemple, l'une des implémentations :

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.neural_network import MLPClassifier
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType

# Chargement des données
file_path = '....csv'
data = pd.read_csv(file_path)

# Divisé en attributs (X) et en étiquettes (y)
X = data.iloc[:, :160].values  # Les 160 premières colonnes sont des données d'entrée
y = data.iloc[:, 160].values   # La dernière colonne est l'étiquette de la cible

# Encodage des libellés de chaînes en libellés numériques
label_encoder = LabelEncoder()
y_encoded = label_encoder.fit_transform(y)

# Normalisation des données
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Diviser les échantillons en échantillons de formation et de test
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y_encoded, test_size=0.2, random_state=42)

# Création d'un classificateur MLP
mlp = MLPClassifier(hidden_layer_sizes=(128, 64), activation='relu', solver='adam', max_iter=500, random_state=42)
mlp.fit(X_train, y_train)

# Estimation de la précision du modèle
accuracy = mlp.score(X_test, y_test)
print(f"Accuracy: {accuracy * 100:.2 f}%")

# Sauvegarde du modèle au format ONNX
initial_type = [('float_input', FloatTensorType([None, 160]))]  # 160 caractéristiques d'entrée
onnx_model = convert_sklearn(mlp, initial_types=initial_type)

# Sauvegarde du modèle ONNX
onnx_file_path = 'model.onnx'
with open(onnx_file_path, "wb") as f:
    f.write(onnx_model.SerializeToString())

print(f"Model saved as {onnx_file_path}")


c'est-à-dire le modèle lui-même - fonctionnant en Python. Il calcule quelque chose

runfile('....py', wdir='...')
Accuracy: 54.16%
Model saved as model.onnx

Mais le conseiller ne peut pas l'accepter.

 
Dans une communauté saine, la génération de code n'est pas discutée ni même considérée comme un genre. Il n'est pas mauvais de connaître le bogue ONNX, à cause duquel le multiclasse n'est pas supporté.
 
Il n'est pas nécessaire d'en discuter
Ce n'est pas la question.
 
Ivan Butko #:
Il n'est pas nécessaire d'en discuter
Ce n'est pas la question.

Essayez {2,3} ou {3}.

demander au script python d'afficher la bonne dimension de la sortie.

mais plus probablement juste {1}, il renvoie une structure dont les champs correspondent déjà à des sorties.


Par exemple, j'ai pour un classificateur binaire

const long output_shape[] = {1};
   if(!OnnxSetOutputShape(ExtHandle, 0, output_shape))
     {
      Print("OnnxSetOutputShape 1 error ", GetLastError());
      return(INIT_FAILED);
     }
 

Il suffit ensuite de créer une structure dans le code

static vector out(1);

   struct output
     {
      long           label[];
      float          tensor[];
     };

   output out2[];
   
   OnnxRun(ExtHandle, ONNX_DEBUG_LOGS, f, out, out2);

   double sig = out2[0].tensor[1];

où le champ d'étiquettes correspond aux valeurs de la classe et le tenseur aux probabilités.

 
Erreur : label contient les valeurs de classe et tensor contient les probabilités. La dimension de sortie est essentiellement 2,2, mais comme la structure est renvoyée, elle devrait être fixée à 1.
 
Maxim Dmitrievsky #:
Erreur : label contient les valeurs de la classe et tensor contient les probabilités. La dimension de sortie est donc essentiellement 2,2, mais comme la structure est renvoyée, vous devriez mettre 1.
Remerciements

UPD

En ce qui concerne l'essence des architectures : dans l'article, la régression traite de la triangulation. Et la classification semble avoir plus de sens. Et voilà qu'il s'avère qu'il y a un problème avec elle dans la fonctionnalité native.

Subjectivement : si l'objectif est marqué comme le prochain prix (ou une autre indication quantitative), le NS commence à osciller d'un côté à l'autre.

Et si l'objectif est marqué comme achat-vente-maintien, au moins, le NS s'ajuste au nombre d'entrées réussies sans prêter attention à la taille. Et sur la distance, cette "ignorance" est compensée par un nivellement, comme s'il s'agissait d'une thérapie par le bruit. Imho, bien sûr, j'ai peu essayé la classification dans une autre implémentation. C'est pourquoi j'ai voulu ici
 
Ivan Butko #:
Nous vous remercions.
Vous pouvez également visualiser votre maillage via Netron, qui affichera la dimension et le type de sortie.
 
Ivan Butko #:
Merci

UPD

En ce qui concerne les architectures, la régression présentée dans l'article porte sur la triangulation. La classification semble plus logique. Et voilà qu'il s'avère qu'il y a un problème avec la fonctionnalité native.

Subjectivement : si l'objectif est marqué comme le prochain prix (ou une autre indication quantitative), le NS commence à osciller d'un côté à l'autre.

Et si l'objectif est marqué comme achat-vente-maintien, au moins, le NS s'ajuste au nombre d'entrées réussies, sans prêter attention à la taille. Et sur la distance, cette "ignorance" est compensée par un nivellement, comme s'il s'agissait d'une thérapie par le bruit. Imho, bien sûr, j'ai peu essayé la classification dans une autre implémentation. J'ai donc voulu ici

C'est à cela que sert le prétraitement, que vous ne respectez pas :) séparer d'abord les grains de l'ivraie, et ensuite l'entraîner à prédire les grains séparés.

Si le prétraitement est bon, le résultat n'est pas tout à fait nul non plus.

 

Y a-t-il une chance que vous puissiez corriger ce script pour qu'il fonctionne avec les nouvelles versions de Python (3.10-3.12) ?

J'ai beaucoup de problèmes en essayant de le faire fonctionner avec la version 3.9.

tx