diff --git a/Dockerfile b/Dockerfile index a70bbeb..dec4654 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,11 @@ WORKDIR /src # Copier les fichiers nécessaires dans le conteneur COPY requirements.txt /src/requirements.txt +# Installer les dépendances système (graphviz) +RUN apt-get update && apt-get install -y --no-install-recommends \ + graphviz libgraphviz-dev \ + && rm -rf /var/lib/apt/lists/* + # Installer les dépendances RUN pip install --no-cache-dir -r requirements.txt diff --git a/README.md b/README.md index 77f7d34..a4ac5a5 100644 --- a/README.md +++ b/README.md @@ -42,4 +42,44 @@ ydata-profiling : https://github.com/ydataai/ydata-profiling profile = ProfileReport(dataframe, tsmode=True, sortby="date", title="Time-Series EDA") - profile.to_file("report_timeseries.html") \ No newline at end of file + profile.to_file("report_timeseries.html") + + +📉 Erreur moyenne absolue : 308.9832 +Il s'agit de la MAE (Mean Absolute Error), une métrique d'évaluation de ton modèle. + +MAE = moyenne des distances absolues entre les prédictions et les vraies valeurs. + +Une MAE de 308.98 signifie qu'en moyenne, les prédictions du modèle s'écartent de ~309 unités de la valeur réelle. + +Cela peut être acceptable ou énorme selon l'échelle de ta variable cible. Si tu prédis des prix en crypto à 60 000 USD, c'est relativement faible. Si tu prédis des variations de 0.5, c’est catastrophique. + +🧠 Architecture du modèle +C’est un modèle Sequential avec 3 couches Dense (fully connected) : + +Couche Sortie Paramètres +dense (None, 64) 320 +dense_1 (None, 32) 2,080 +dense_2 (None, 1) 33 + +✳️ Détails : +(None, 64) signifie : nombre d'exemples non spécifié (None), chaque exemple produit un vecteur de 64. + +Param # = nombre de poids (W) + biais (b) à apprendre. + +Par exemple : dense_1 a 64 entrées et 32 sorties → (64 * 32) + 32 = 2080 + +🧮 Total : +Paramètres totaux : 7 301 (dont certains sont utilisés pour l’optimiseur) + +Trainables : 2 433 paramètres que le modèle ajuste lors de l’entraînement + +Non-trainables : aucun ici (souvent présents avec des couches gelées ou embeddings fixes) + + +### Netron + +/home/jerome/.local/bin/netron -p 5000 --host 127.0.0.1 /home/jerome/Perso/freqtradeDocker/user_data/reports/model.h5 +Serving '/home/jerome/Perso/freqtradeDocker/user_data/reports/model.h5' at http://127.0.0.1:5000 + + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index dad7a1d..e18924e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,4 +14,6 @@ tensorflow keras scikit-learn pydot -graphviz \ No newline at end of file +graphviz +ann_visualizer +netron \ No newline at end of file diff --git a/src/app.py b/src/app.py index f48d625..97b8f27 100644 --- a/src/app.py +++ b/src/app.py @@ -15,6 +15,10 @@ from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense from tensorflow.keras.utils import plot_model +from keras.models import Sequential +from keras.layers import Dense +from ann_visualizer.visualize import ann_viz + app = Flask(__name__) FREQTRADE_USERDATA_DIR = '/mnt/external' @@ -128,43 +132,6 @@ def read_feather(filename): # dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) # dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) - # Choisir les colonnes techniques comme variables d'entrée (X) - feature_cols = ['rsi', 'sma20', 'sma5_1h', 'volume'] - df = dataframe - # Variable cible - df['target'] = df['futur_price_1h'] - - # Supprimer les lignes avec des NaN - df.dropna(subset=feature_cols + ['target'], inplace=True) - - X = df[feature_cols].values - y = df['target'].values - - # Normalisation - scaler = StandardScaler() - X = scaler.fit_transform(X) - - # Split - X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) - - # Modèle - model = Sequential([ - Dense(64, input_dim=X.shape[1], activation='relu'), - Dense(32, activation='relu'), - Dense(1) # Prédiction continue - ]) - - model.compile(optimizer='adam', loss='mse', metrics=['mae']) - - # Entraînement - model.fit(X_train, y_train, epochs=50, batch_size=32, validation_data=(X_test, y_test)) - - loss, mae = model.evaluate(X_test, y_test) - print(f"Erreur moyenne absolue : {mae:.4f}") - - model.summary() - - plot_model(model, show_shapes=True, show_layer_names=True, to_file=FREQTRADE_USERDATA_DIR + "/reports/model.png") return dataframe.to_json(orient="records") except Exception as e: print(e) @@ -229,8 +196,57 @@ def get_chart_data(): return df.to_json(orient="records") #jsonify(chart_data) -@app.route('/model') -def show_model(): + +@app.route('/generate_model') +def generate_model(): + filename = request.args.get('filename', '') + path = os.path.join(FREQTRADE_USERDATA_DIR + "/data/binance/", filename) + print(path) + # indicators = request.args.get('indicators', '').split(',') + df = pd.read_feather(path) + + # Choisir les colonnes techniques comme variables d'entrée (X) + feature_cols = ['close', 'rsi', 'sma5', 'sma10', 'sma20', 'sma5_1h', 'volume', 'sma5_1h'] + + # Variable cible 2 heures + df['target'] = (df['close'].shift(-24) - df['close']) / df['close'] + + # Supprimer les lignes avec des NaN + df.dropna(subset=feature_cols + ['target'], inplace=True) + + X = df[feature_cols].values + y = df['target'].values + + # Normalisation + scaler = StandardScaler() + X = scaler.fit_transform(X) + + # Split + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) + + # Modèle + model = Sequential([ + Dense(64, input_dim=X.shape[1], activation='relu'), + Dense(32, activation='relu'), + Dense(1) # Prédiction continue + ]) + + model.compile(optimizer='adam', loss='mse', metrics=['mae']) + + # Entraînement + model.fit(X_train, y_train, epochs=100, batch_size=64, validation_data=(X_test, y_test)) + + loss, mae = model.evaluate(X_test, y_test) + print(f"Erreur moyenne absolue : {mae:.4f}") + + model.summary() + + plot_model(model, show_shapes=True, show_layer_names=True, to_file=FREQTRADE_USERDATA_DIR + "/reports/model.png") + + model.save(FREQTRADE_USERDATA_DIR + "/reports/model.h5") + + # ann_viz(model, title="Mon réseau", filename=FREQTRADE_USERDATA_DIR + "/reports/network.gv", view=True) + # Créer un exemple de modèle si non encore généré model_path = FREQTRADE_USERDATA_DIR + "/reports/model.png" if not os.path.exists(model_path): @@ -242,6 +258,7 @@ def show_model(): plot_model(model, to_file=model_path, show_shapes=True, show_layer_names=True) return render_template('model.html', model_image=model_path) + # Route pour servir les fichiers statiques (optionnelle si bien configuré) @app.route('/static/') def static_files(filename): diff --git a/src/static/js/functions.js b/src/static/js/functions.js index 6156a18..5e99a12 100644 --- a/src/static/js/functions.js +++ b/src/static/js/functions.js @@ -547,6 +547,16 @@ function initReport(data) { indicators.innerHTML = string } +function generateModel() { + + const element = document.getElementById('current_file_name'); + let filename = element.value + const indicators = Array.from(selectedReportIndicators).join(','); + + fetch(`/generate_model?filename=${filename}`) + .then(alert('Generation en cours')); +} + function generateReport() { const element = document.getElementById('current_file_name'); diff --git a/src/templates/index.html b/src/templates/index.html index d0e1ff6..5ee114d 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -52,7 +52,7 @@ - Voir le modèle +
diff --git a/src/test/dot.py b/src/test/dot.py new file mode 100644 index 0000000..fb08686 --- /dev/null +++ b/src/test/dot.py @@ -0,0 +1,12 @@ +from tensorflow.keras.models import Sequential +from tensorflow.keras.layers import Dense +from tensorflow.keras.utils import plot_model + +# Créer un modèle simple +model = Sequential() +model.add(Dense(64, input_dim=5, activation='relu')) +model.add(Dense(32, activation='relu')) +model.add(Dense(1, activation='linear')) + +# Générer l'image du modèle +plot_model(model, to_file='/mnt/external/reports/model.png', show_shapes=True, show_layer_names=True) diff --git a/src/test/kera.py b/src/test/kera.py new file mode 100644 index 0000000..f8005c6 --- /dev/null +++ b/src/test/kera.py @@ -0,0 +1,11 @@ +import pydot +from keras.utils import plot_model +from keras.models import Sequential +from keras.layers import Dense + +model = Sequential([ + Dense(4, input_shape=(3,), activation='relu'), + Dense(2, activation='softmax') +]) + +plot_model(model, to_file='model_test.png', show_shapes=True)