Przejdź do treści
AI & ML

Machine Learning z Python i TensorFlow - Praktyczny przewodnik

Opublikowano:
·5 min czytania·Autor: MDS Software Solutions Group

Machine Learning Python

ai-ml

Machine Learning z Python i TensorFlow - Praktyczny przewodnik

Machine Learning (uczenie maszynowe) rewolucjonizuje sposób, w jaki budujemy oprogramowanie. Zamiast programowania jawnych reguł, uczymy modele rozpoznawania wzorców w danych. TensorFlow, stworzony przez Google, jest jednym z najpotężniejszych frameworków do budowy i wdrażania modeli ML. W tym przewodniku przejdziemy od podstaw teoretycznych, przez architekturę TensorFlow, po praktyczne implementacje i deployment modeli produkcyjnych.

Podstawy Machine Learning#

Zanim zagłębimy się w TensorFlow, warto zrozumieć trzy główne paradygmaty uczenia maszynowego.

Uczenie nadzorowane (Supervised Learning)#

W uczeniu nadzorowanym model trenowany jest na parach (dane wejściowe, oczekiwany wynik). Na podstawie tych przykładów uczy się funkcji mapującej wejście na wyjście.

Typowe zastosowania:

  • Klasyfikacja - rozpoznawanie obrazów, analiza sentymentu, wykrywanie spamu
  • Regresja - przewidywanie cen, prognoza pogody, estymacja czasu dostawy
# Prosty przykład: klasyfikacja binarna
import tensorflow as tf
from tensorflow import keras

# Dane treningowe: cechy i etykiety
X_train = [[0.1, 0.2], [0.4, 0.6], [0.8, 0.9], [0.3, 0.1]]
y_train = [0, 0, 1, 0]  # 0 = klasa A, 1 = klasa B

model = keras.Sequential([
    keras.layers.Dense(16, activation='relu', input_shape=(2,)),
    keras.layers.Dense(1, activation='sigmoid')
])

model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
model.fit(X_train, y_train, epochs=50, verbose=0)

Uczenie nienadzorowane (Unsupervised Learning)#

Model analizuje dane bez etykiet, szukając ukrytych wzorców i struktur.

Typowe zastosowania:

  • Klasteryzacja - grupowanie klientów, segmentacja obrazów
  • Redukcja wymiarów - wizualizacja danych wielowymiarowych (PCA, t-SNE)
  • Wykrywanie anomalii - identyfikacja oszustw, monitoring systemów
# Autoenkoder do wykrywania anomalii
encoder = keras.Sequential([
    keras.layers.Dense(64, activation='relu', input_shape=(100,)),
    keras.layers.Dense(32, activation='relu'),
    keras.layers.Dense(16, activation='relu')  # Bottleneck
])

decoder = keras.Sequential([
    keras.layers.Dense(32, activation='relu', input_shape=(16,)),
    keras.layers.Dense(64, activation='relu'),
    keras.layers.Dense(100, activation='sigmoid')
])

autoencoder_input = keras.layers.Input(shape=(100,))
encoded = encoder(autoencoder_input)
decoded = decoder(encoded)
autoencoder = keras.Model(autoencoder_input, decoded)

autoencoder.compile(optimizer='adam', loss='mse')

Uczenie ze wzmocnieniem (Reinforcement Learning)#

Agent uczy się podejmowania decyzji w środowisku, otrzymując nagrody za dobre działania i kary za złe. Jest to paradygmat za sukcesem AlphaGo, robotyki i autonomicznych pojazdów.

Kluczowe pojecia:

  • Agent - podmiot podejmujący decyzje
  • Środowisko - świat, w którym działa agent
  • Polityka (Policy) - strategia decyzyjna agenta
  • Nagroda (Reward) - sygnał zwrotny ze środowiska

Architektura TensorFlow i Keras#

TensorFlow to kompleksowy ekosystem do uczenia maszynowego. Keras, będący wysokopoziomowym API TensorFlow, znacząco upraszcza budowę modeli.

Tensory - podstawowa struktura danych#

Tensor to wielowymiarowa tablica liczbowa. Nazwa TensorFlow pochodzi od przepływu tensorów przez graf obliczeniowy.

import tensorflow as tf

# Skalar (tensor 0-wymiarowy)
scalar = tf.constant(42)
print(scalar.shape)  # ()

# Wektor (tensor 1-wymiarowy)
vector = tf.constant([1.0, 2.0, 3.0])
print(vector.shape)  # (3,)

# Macierz (tensor 2-wymiarowy)
matrix = tf.constant([[1, 2], [3, 4], [5, 6]])
print(matrix.shape)  # (3, 2)

# Tensor 3-wymiarowy (np. batch obrazów RGB)
images = tf.random.normal([32, 224, 224, 3])
print(images.shape)  # (32, 224, 224, 3)

# Operacje na tensorach
a = tf.constant([[1, 2], [3, 4]])
b = tf.constant([[5, 6], [7, 8]])

print(tf.add(a, b))       # Element-wise addition
print(tf.matmul(a, b))    # Mnożenie macierzy
print(tf.reduce_mean(a))  # Średnia

Warstwy (Layers)#

Warstwy to fundamentalne bloki konstrukcyjne modeli. Każda warstwa wykonuje transformację danych wejściowych.

# Najpopularniejsze warstwy w Keras

# Dense - warstwa w pełni połączona
dense = keras.layers.Dense(128, activation='relu')

# Conv2D - warstwa konwolucyjna 2D
conv = keras.layers.Conv2D(64, kernel_size=(3, 3), activation='relu', padding='same')

# LSTM - Long Short-Term Memory
lstm = keras.layers.LSTM(256, return_sequences=True)

# Dropout - regularyzacja (zapobiega overfitting)
dropout = keras.layers.Dropout(0.3)

# BatchNormalization - normalizacja wsadowa
bn = keras.layers.BatchNormalization()

# Embedding - mapowanie indeksów na wektory gęste
embedding = keras.layers.Embedding(input_dim=10000, output_dim=128)

Modele - Sequential vs Functional API#

Keras oferuje dwa główne sposoby budowy modeli.

# 1. Sequential API - prosty stos warstw
model_sequential = keras.Sequential([
    keras.layers.Dense(256, activation='relu', input_shape=(784,)),
    keras.layers.Dropout(0.3),
    keras.layers.Dense(128, activation='relu'),
    keras.layers.Dropout(0.2),
    keras.layers.Dense(10, activation='softmax')
])

# 2. Functional API - elastyczne grafy (wiele wejść/wyjść, skip connections)
inputs = keras.layers.Input(shape=(784,))
x = keras.layers.Dense(256, activation='relu')(inputs)
x = keras.layers.Dropout(0.3)(x)

# Skip connection (residual)
skip = keras.layers.Dense(128)(inputs)
x = keras.layers.Dense(128, activation='relu')(x)
x = keras.layers.Add()([x, skip])

x = keras.layers.Dropout(0.2)(x)
outputs = keras.layers.Dense(10, activation='softmax')(x)

model_functional = keras.Model(inputs=inputs, outputs=outputs)

Pętla treningowa (Training Loop)#

Proces trenowania modelu składa się z kilku etapów: kompilacja, trening, ewaluacja.

# Kompilacja modelu
model.compile(
    optimizer=keras.optimizers.Adam(learning_rate=0.001),
    loss='sparse_categorical_crossentropy',
    metrics=['accuracy']
)

# Callbacki - monitorowanie i sterowanie treningiem
callbacks = [
    keras.callbacks.EarlyStopping(
        monitor='val_loss',
        patience=5,
        restore_best_weights=True
    ),
    keras.callbacks.ReduceLROnPlateau(
        monitor='val_loss',
        factor=0.5,
        patience=3
    ),
    keras.callbacks.ModelCheckpoint(
        filepath='best_model.keras',
        monitor='val_accuracy',
        save_best_only=True
    ),
    keras.callbacks.TensorBoard(log_dir='./logs')
]

# Trening
history = model.fit(
    X_train, y_train,
    epochs=100,
    batch_size=32,
    validation_split=0.2,
    callbacks=callbacks
)

# Ewaluacja na danych testowych
test_loss, test_acc = model.evaluate(X_test, y_test)
print(f"Test accuracy: {test_acc:.4f}")

Popularne architektury sieci neuronowych#

CNN - Convolutional Neural Networks#

Sieci konwolucyjne dominują w przetwarzaniu obrazów. Filtry konwolucyjne automatycznie uczą się wykrywania cech wizualnych.

def build_cnn_classifier(input_shape=(224, 224, 3), num_classes=10):
    """Budowa klasyfikatora obrazów CNN."""
    model = keras.Sequential([
        # Blok konwolucyjny 1
        keras.layers.Conv2D(32, (3, 3), activation='relu',
                           padding='same', input_shape=input_shape),
        keras.layers.Conv2D(32, (3, 3), activation='relu', padding='same'),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.BatchNormalization(),

        # Blok konwolucyjny 2
        keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
        keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.BatchNormalization(),

        # Blok konwolucyjny 3
        keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same'),
        keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same'),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.BatchNormalization(),
        keras.layers.Dropout(0.3),

        # Klasyfikator
        keras.layers.GlobalAveragePooling2D(),
        keras.layers.Dense(256, activation='relu'),
        keras.layers.Dropout(0.5),
        keras.layers.Dense(num_classes, activation='softmax')
    ])

    model.compile(
        optimizer='adam',
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model

# Użycie
cnn_model = build_cnn_classifier(num_classes=10)
cnn_model.summary()

Kluczowe warstwy CNN:

  • Conv2D - wykrywanie lokalnych cech (krawędzie, tekstury, kształty)
  • MaxPooling2D - redukcja wymiarów przestrzennych, zwiększanie pola widzenia
  • GlobalAveragePooling2D - agregacja map cech do wektora
  • BatchNormalization - stabilizacja treningu, szybsza zbieżność

RNN - Recurrent Neural Networks#

Sieci rekurencyjne przetwarzają dane sekwencyjne - tekst, szeregi czasowe, dźwięk. LSTM i GRU rozwiązują problem zanikającego gradientu.

def build_text_classifier(vocab_size=20000, max_length=200, num_classes=5):
    """Klasyfikator tekstu z dwukierunkowym LSTM."""
    model = keras.Sequential([
        keras.layers.Embedding(vocab_size, 128, input_length=max_length),
        keras.layers.Bidirectional(
            keras.layers.LSTM(64, return_sequences=True)
        ),
        keras.layers.Bidirectional(keras.layers.LSTM(32)),
        keras.layers.Dense(64, activation='relu'),
        keras.layers.Dropout(0.5),
        keras.layers.Dense(num_classes, activation='softmax')
    ])

    model.compile(
        optimizer='adam',
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model

# Generowanie tekstu z RNN
def build_text_generator(vocab_size, seq_length):
    """Model generowania tekstu znak po znaku."""
    model = keras.Sequential([
        keras.layers.Embedding(vocab_size, 256, input_length=seq_length),
        keras.layers.LSTM(512, return_sequences=True),
        keras.layers.LSTM(512),
        keras.layers.Dense(256, activation='relu'),
        keras.layers.Dense(vocab_size, activation='softmax')
    ])
    model.compile(optimizer='adam', loss='sparse_categorical_crossentropy')
    return model

Transformer - rewolucja w NLP i nie tylko#

Architektura Transformer, oparta na mechanizmie self-attention, zrewolucjonizowała NLP i coraz szerzej wkracza w wizję komputerową.

class TransformerBlock(keras.layers.Layer):
    """Blok Transformer z Multi-Head Attention."""

    def __init__(self, embed_dim, num_heads, ff_dim, dropout_rate=0.1):
        super().__init__()
        self.attention = keras.layers.MultiHeadAttention(
            num_heads=num_heads,
            key_dim=embed_dim // num_heads
        )
        self.ffn = keras.Sequential([
            keras.layers.Dense(ff_dim, activation='gelu'),
            keras.layers.Dense(embed_dim)
        ])
        self.layernorm1 = keras.layers.LayerNormalization(epsilon=1e-6)
        self.layernorm2 = keras.layers.LayerNormalization(epsilon=1e-6)
        self.dropout1 = keras.layers.Dropout(dropout_rate)
        self.dropout2 = keras.layers.Dropout(dropout_rate)

    def call(self, inputs, training=False):
        # Multi-Head Self-Attention + Residual
        attn_output = self.attention(inputs, inputs)
        attn_output = self.dropout1(attn_output, training=training)
        out1 = self.layernorm1(inputs + attn_output)

        # Feed-Forward Network + Residual
        ffn_output = self.ffn(out1)
        ffn_output = self.dropout2(ffn_output, training=training)
        return self.layernorm2(out1 + ffn_output)


def build_transformer_classifier(
    vocab_size=20000, max_length=200, embed_dim=128,
    num_heads=4, ff_dim=256, num_blocks=2, num_classes=5
):
    """Klasyfikator tekstu oparty na Transformer."""
    inputs = keras.layers.Input(shape=(max_length,))
    x = keras.layers.Embedding(vocab_size, embed_dim)(inputs)

    # Positional encoding (prosty - learned)
    positions = keras.layers.Embedding(max_length, embed_dim)(
        tf.range(max_length)
    )
    x = x + positions

    for _ in range(num_blocks):
        x = TransformerBlock(embed_dim, num_heads, ff_dim)(x)

    x = keras.layers.GlobalAveragePooling1D()(x)
    x = keras.layers.Dense(64, activation='relu')(x)
    x = keras.layers.Dropout(0.3)(x)
    outputs = keras.layers.Dense(num_classes, activation='softmax')(x)

    model = keras.Model(inputs=inputs, outputs=outputs)
    model.compile(
        optimizer=keras.optimizers.Adam(learning_rate=1e-4),
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model

Transfer Learning - potega pretrenowanych modeli#

Transfer learning pozwala wykorzystac wiedzę z modeli trenowanych na ogromnych zbiorach danych. Zamiast trenowania od zera, dostrajamy pretrenowany model do naszego zadania.

def build_transfer_learning_model(num_classes, fine_tune_at=100):
    """
    Klasyfikator obrazów z transfer learning (EfficientNetV2).
    Trenujemy tylko wierzchnie warstwy, zamrażając bazę.
    """
    # Pretrenowany model jako ekstraktor cech
    base_model = keras.applications.EfficientNetV2B0(
        weights='imagenet',
        include_top=False,
        input_shape=(224, 224, 3)
    )

    # Zamrożenie warstw bazowych
    base_model.trainable = False

    # Budowa modelu
    model = keras.Sequential([
        base_model,
        keras.layers.GlobalAveragePooling2D(),
        keras.layers.Dense(256, activation='relu'),
        keras.layers.Dropout(0.3),
        keras.layers.Dense(num_classes, activation='softmax')
    ])

    model.compile(
        optimizer=keras.optimizers.Adam(learning_rate=1e-3),
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model, base_model


def fine_tune_model(model, base_model, fine_tune_at=100):
    """
    Etap 2: Fine-tuning - odmrażamy górne warstwy bazowego modelu.
    Używamy niższego learning rate, aby nie zniszczyć pretrenowanych wag.
    """
    base_model.trainable = True
    for layer in base_model.layers[:fine_tune_at]:
        layer.trainable = False

    model.compile(
        optimizer=keras.optimizers.Adam(learning_rate=1e-5),
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model


# Użycie
model, base_model = build_transfer_learning_model(num_classes=5)

# Etap 1: Trening klasyfikatora (zamrożona baza)
model.fit(train_ds, epochs=10, validation_data=val_ds)

# Etap 2: Fine-tuning (odmrożone górne warstwy)
model = fine_tune_model(model, base_model, fine_tune_at=100)
model.fit(train_ds, epochs=10, validation_data=val_ds)

Popularne pretrenowane modele w TensorFlow:

  • EfficientNetV2 - najlepsza równowaga dokładności i wydajności dla obrazów
  • ResNet50/101 - klasyczna architektura z residual connections
  • MobileNetV3 - zoptymalizowany dla urządzeń mobilnych
  • BERT/DistilBERT - pretrenowane modele językowe (przez TF Hub lub Hugging Face)

Praktyczny przyklad: klasyfikacja obrazów#

Zbudujmy kompletny pipeline klasyfikacji obrazów z augmentacją danych i transfer learningiem.

import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt

# 1. Ładowanie i przygotowanie danych
BATCH_SIZE = 32
IMG_SIZE = (224, 224)

train_ds = keras.utils.image_dataset_from_directory(
    'dataset/train',
    image_size=IMG_SIZE,
    batch_size=BATCH_SIZE,
    label_mode='int',
    shuffle=True,
    seed=42
)

val_ds = keras.utils.image_dataset_from_directory(
    'dataset/validation',
    image_size=IMG_SIZE,
    batch_size=BATCH_SIZE,
    label_mode='int'
)

class_names = train_ds.class_names
num_classes = len(class_names)

# 2. Augmentacja danych
data_augmentation = keras.Sequential([
    keras.layers.RandomFlip('horizontal'),
    keras.layers.RandomRotation(0.2),
    keras.layers.RandomZoom(0.2),
    keras.layers.RandomContrast(0.2),
    keras.layers.RandomTranslation(0.1, 0.1)
])

# 3. Optymalizacja pipeline'u danych
AUTOTUNE = tf.data.AUTOTUNE
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

# 4. Budowa modelu z transfer learning
base_model = keras.applications.EfficientNetV2B0(
    weights='imagenet', include_top=False, input_shape=(*IMG_SIZE, 3)
)
base_model.trainable = False

inputs = keras.layers.Input(shape=(*IMG_SIZE, 3))
x = data_augmentation(inputs)
x = keras.applications.efficientnet_v2.preprocess_input(x)
x = base_model(x, training=False)
x = keras.layers.GlobalAveragePooling2D()(x)
x = keras.layers.Dense(256, activation='relu')(x)
x = keras.layers.Dropout(0.3)(x)
outputs = keras.layers.Dense(num_classes, activation='softmax')(x)

model = keras.Model(inputs, outputs)

# 5. Trening
model.compile(
    optimizer=keras.optimizers.Adam(1e-3),
    loss='sparse_categorical_crossentropy',
    metrics=['accuracy']
)

history = model.fit(
    train_ds, epochs=20, validation_data=val_ds,
    callbacks=[
        keras.callbacks.EarlyStopping(patience=5, restore_best_weights=True),
        keras.callbacks.ReduceLROnPlateau(patience=3, factor=0.5)
    ]
)

# 6. Wizualizacja wyników
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
ax1.plot(history.history['accuracy'], label='Train')
ax1.plot(history.history['val_accuracy'], label='Val')
ax1.set_title('Accuracy')
ax1.legend()

ax2.plot(history.history['loss'], label='Train')
ax2.plot(history.history['val_loss'], label='Val')
ax2.set_title('Loss')
ax2.legend()
plt.savefig('training_history.png', dpi=150)
plt.show()

Praktyczny przyklad: analiza sentymentu (NLP)#

import tensorflow as tf
from tensorflow import keras
import tensorflow_datasets as tfds

# 1. Ładowanie zbioru IMDB Reviews
(train_data, test_data), info = tfds.load(
    'imdb_reviews',
    split=['train', 'test'],
    as_supervised=True,
    with_info=True
)

# 2. Tokenizacja i przygotowanie danych
VOCAB_SIZE = 20000
MAX_LENGTH = 300

# TextVectorization - wbudowana warstwa tokenizacji
text_vectorizer = keras.layers.TextVectorization(
    max_tokens=VOCAB_SIZE,
    output_mode='int',
    output_sequence_length=MAX_LENGTH
)

# Dostosowanie do danych treningowych
train_texts = train_data.map(lambda text, label: text)
text_vectorizer.adapt(train_texts)

# 3. Przygotowanie datasetów
def preprocess(text, label):
    text = text_vectorizer(tf.expand_dims(text, -1))
    text = tf.squeeze(text, axis=0)
    return text, label

BATCH_SIZE = 64
AUTOTUNE = tf.data.AUTOTUNE

train_ds = (train_data
    .map(preprocess, num_parallel_calls=AUTOTUNE)
    .shuffle(10000)
    .batch(BATCH_SIZE)
    .prefetch(AUTOTUNE))

test_ds = (test_data
    .map(preprocess, num_parallel_calls=AUTOTUNE)
    .batch(BATCH_SIZE)
    .prefetch(AUTOTUNE))

# 4. Model: 1D CNN + Bidirectional LSTM
model = keras.Sequential([
    keras.layers.Embedding(VOCAB_SIZE, 128, input_length=MAX_LENGTH),
    keras.layers.Conv1D(128, 5, activation='relu', padding='same'),
    keras.layers.MaxPooling1D(2),
    keras.layers.Bidirectional(keras.layers.LSTM(64)),
    keras.layers.Dense(64, activation='relu'),
    keras.layers.Dropout(0.5),
    keras.layers.Dense(1, activation='sigmoid')
])

model.compile(
    optimizer='adam',
    loss='binary_crossentropy',
    metrics=['accuracy']
)

# 5. Trening
history = model.fit(
    train_ds, epochs=10, validation_data=test_ds,
    callbacks=[keras.callbacks.EarlyStopping(patience=3)]
)

# 6. Predykcja
def predict_sentiment(texts):
    """Predykcja sentymentu dla listy tekstów."""
    vectorized = text_vectorizer(tf.constant(texts))
    predictions = model.predict(vectorized)
    for text, pred in zip(texts, predictions):
        sentiment = "Pozytywny" if pred[0] > 0.5 else "Negatywny"
        print(f"{sentiment} ({pred[0]:.2%}): {text[:80]}...")

predict_sentiment([
    "This movie was absolutely fantastic! Great acting and storyline.",
    "Terrible film. Waste of time. The plot made no sense at all."
])

TensorFlow.js - Machine Learning w przegladarce#

TensorFlow.js pozwala uruchamiać modele ML bezpośrednio w przeglądarce lub w Node.js, umożliwiając inferencję po stronie klienta bez komunikacji z serwerem.

// Instalacja: npm install @tensorflow/tfjs

import * as tf from '@tensorflow/tfjs';

// Ładowanie pretrenowanego modelu do klasyfikacji obrazów
async function classifyImage(imageElement) {
    // Ładowanie modelu MobileNet
    const model = await tf.loadGraphModel(
        'https://tfhub.dev/google/tfjs-model/imagenet/mobilenet_v3_small_100_224/classification/5/default/1',
        { fromTFHub: true }
    );

    // Przetworzenie obrazu na tensor
    const tensor = tf.browser.fromPixels(imageElement)
        .resizeBilinear([224, 224])
        .expandDims(0)
        .div(255.0);

    // Inferencja
    const predictions = model.predict(tensor);
    const topK = tf.topk(predictions, 5);

    const indices = await topK.indices.data();
    const values = await topK.values.data();

    // Zwolnienie pamięci
    tensor.dispose();
    predictions.dispose();

    return indices.map((idx, i) => ({
        classIndex: idx,
        confidence: values[i]
    }));
}

// Konwersja modelu Keras do TensorFlow.js
// W Pythonie:
// import tensorflowjs as tfjs
// tfjs.converters.save_keras_model(model, 'tfjs_model/')

Zalety TensorFlow.js:

  • Prywatność - dane nie opuszczają urządzenia użytkownika
  • Niskie opóźnienia - brak round-trip do serwera
  • Offline - model działa bez internetu
  • Skalowanie - inferencja na urządzeniach użytkowników zamiast kosztownych GPU

Deployment modeli produkcyjnych#

TensorFlow Serving#

TensorFlow Serving to wydajny system do serwowania modeli w produkcji.

# Zapisanie modelu w formacie SavedModel
model.save('saved_model/image_classifier/1')

# Struktura katalogów:
# saved_model/
#   image_classifier/
#     1/                    <- wersja modelu
#       saved_model.pb
#       variables/
# docker-compose.yml - TensorFlow Serving
version: "3.8"
services:
  tf-serving:
    image: tensorflow/serving:latest
    ports:
      - "8501:8501"  # REST API
      - "8500:8500"  # gRPC
    volumes:
      - ./saved_model:/models
    environment:
      MODEL_NAME: image_classifier
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
# Wywołanie REST API
import requests
import numpy as np
import json

def predict_via_serving(image_array):
    """Wysłanie zapytania do TensorFlow Serving."""
    url = "http://localhost:8501/v1/models/image_classifier:predict"
    payload = {
        "instances": image_array.tolist()
    }
    response = requests.post(url, json=payload)
    predictions = response.json()['predictions']
    return np.array(predictions)

# Batch prediction
images = np.random.rand(8, 224, 224, 3).astype(np.float32)
results = predict_via_serving(images)
print(f"Predictions shape: {results.shape}")

Eksport do ONNX#

ONNX (Open Neural Network Exchange) to otwarty format pozwalający uruchamiać modele w różnych runtime'ach.

# Konwersja TensorFlow -> ONNX
# pip install tf2onnx

import tf2onnx
import onnxruntime as ort

# Konwersja
spec = (tf.TensorSpec((None, 224, 224, 3), tf.float32, name="input"),)
model_proto, _ = tf2onnx.convert.from_keras(
    model, input_signature=spec, output_path="model.onnx"
)

# Inferencja z ONNX Runtime (szybsza niż TF dla CPU)
session = ort.InferenceSession("model.onnx")
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name

result = session.run(
    [output_name],
    {input_name: np.random.rand(1, 224, 224, 3).astype(np.float32)}
)
print(f"ONNX prediction: {result[0]}")

TensorFlow Lite - modele na urządzeniach mobilnych#

# Konwersja do TFLite z kwantyzacją
converter = tf.lite.TFLiteConverter.from_saved_model('saved_model/image_classifier/1')

# Kwantyzacja post-treningowa (zmniejsza model 4x)
converter.optimizations = [tf.lite.Optimize.DEFAULT]

# Opcjonalnie: pełna kwantyzacja int8 (wymaga kalibracji)
def representative_dataset():
    for i in range(100):
        yield [np.random.rand(1, 224, 224, 3).astype(np.float32)]

converter.representative_dataset = representative_dataset
converter.target_spec.supported_types = [tf.float16]

tflite_model = converter.convert()

with open('model.tflite', 'wb') as f:
    f.write(tflite_model)

print(f"TFLite model size: {len(tflite_model) / 1024 / 1024:.2f} MB")

TensorFlow vs PyTorch - porownanie#

Wybór frameworka zależy od kontekstu projektu. Oto kluczowe różnice:

| Aspekt | TensorFlow | PyTorch | |--------|-----------|---------| | Tryb wykonania | Eager + Graph (tf.function) | Eager domyślnie, TorchScript dla grafu | | API wysokopoziomowe | Keras (wbudowane) | torch.nn (wbudowane), Lightning | | Deployment | TF Serving, TFLite, TF.js | TorchServe, ONNX, ExecuTorch | | Mobilne/Edge | TFLite (dojrzały) | ExecuTorch (nowszy) | | Przeglądarka | TensorFlow.js (dojrzały) | ONNX.js / Transformers.js | | Ekosystem | TFX, TF Hub, TF Data | Hugging Face, PyTorch Ecosystem | | Popularność w badaniach | Spadająca | Dominująca | | Popularność w produkcji | Nadal silna | Rosnąca | | Debugging | Trudniejszy (graf) | Łatwiejszy (Pythonic) | | Dokumentacja | Obszerna, ale rozproszona | Spójna, dobrze zorganizowana |

Kiedy wybrać TensorFlow:

  • Deployment na wielu platformach (web, mobile, embedded)
  • Istniejąca infrastruktura TFX w organizacji
  • Potrzeba TensorFlow.js do inferencji w przeglądarce
  • Projekty produkcyjne wymagające TF Serving

Kiedy wybrać PyTorch:

  • Badania naukowe i prototypowanie
  • Praca z modelami z Hugging Face
  • Preferowanie Pythonic API i łatwego debugowania
  • Projekty wymagające najnowszych architektur z papierów naukowych

Dobre praktyki w projektach ML#

1. Zarządzanie eksperymentami#

import mlflow

# Logowanie eksperymentów z MLflow
mlflow.set_experiment("image-classification")

with mlflow.start_run(run_name="efficientnet-v2-finetune"):
    # Logowanie hiperparametrów
    mlflow.log_params({
        "learning_rate": 1e-3,
        "batch_size": 32,
        "epochs": 20,
        "model": "EfficientNetV2B0",
        "fine_tune_layers": 50
    })

    # Trening
    history = model.fit(train_ds, epochs=20, validation_data=val_ds)

    # Logowanie metryk
    mlflow.log_metrics({
        "final_accuracy": history.history['accuracy'][-1],
        "final_val_accuracy": history.history['val_accuracy'][-1],
        "final_loss": history.history['loss'][-1]
    })

    # Logowanie modelu
    mlflow.keras.log_model(model, "model")

2. Wersjonowanie danych#

# DVC (Data Version Control) - śledzenie zmian w danych
# dvc init
# dvc add dataset/
# git add dataset.dvc .gitignore
# git commit -m "Add training dataset v1"
# dvc push

3. Reprodukowalność#

import random
import numpy as np

def set_seeds(seed=42):
    """Ustawienie seedów dla reprodukowalności."""
    random.seed(seed)
    np.random.seed(seed)
    tf.random.set_seed(seed)
    # Deterministic operations (wolniejsze, ale reprodukowalne)
    tf.config.experimental.enable_op_determinism()

set_seeds(42)

Podsumowanie#

TensorFlow w połączeniu z Keras oferuje kompletny ekosystem do budowy, trenowania i wdrażania modeli Machine Learning. Od prostych sieci Dense, przez zaawansowane architektury CNN, RNN i Transformer, po deployment na serwerach, urządzeniach mobilnych i w przeglądarkach - TensorFlow dostarcza narzędzia na każdym etapie cyklu życia modelu ML.

Kluczowe wnioski:

  • Zacznij od transfer learningu - pretrenowane modele dają świetne wyniki przy minimalnej ilości danych
  • Optymalizuj pipeline danych - tf.data z prefetch i cache znacząco przyspiesza trening
  • Monitoruj eksperymenty - MLflow lub TensorBoard to konieczność w poważnych projektach
  • Testuj deployment wcześnie - TF Serving, TFLite i TF.js mają różne ograniczenia

Potrzebujesz wdrożyć Machine Learning w swojej aplikacji? Zespół MDS Software Solutions Group specjalizuje się w budowie inteligentnych rozwiązań opartych na AI i ML. Od klasyfikacji obrazów, przez analizę tekstu, po systemy rekomendacji - pomożemy Ci przejść od prototypu do produkcji. Skontaktuj się z nami i porozmawiajmy o Twoim projekcie!

Autor
MDS Software Solutions Group

Zespół ekspertów programistycznych specjalizujących się w nowoczesnych technologiach webowych.

Machine Learning z Python i TensorFlow - Praktyczny przewodnik | MDS Software Solutions Group | MDS Software Solutions Group