Este texto explica um modelo híbrido de machine learning. Ele combina redes convolucionais (CNNs) com aprendizado semi-supervisionado. Primeiro, a CNN é ajustada (fine-tuning) com poucos dados rotulados. Depois, ela aprende com muitos dados não rotulados. Assim, o modelo ganha precisão sem esforço manual enorme.
Características principais do modelo
Primeiramente, a arquitetura usa uma CNN pré-treinada (como ResNet ou VGG). Suas camadas convolucionais extraem características visuais. Em seguida, camadas totalmente conectadas fazem a classificação. A parte semi-supervisionada emprega pseudo-rotulação ou consistência. Consequentemente, o modelo reduz o overfitting em dados escassos. Os hiperparâmetros incluem taxa de aprendizado (0.0001 a 0.001). O peso da perda não supervisionada (λ) é tipicamente 0.5. A temperatura para suavização de pseudo-rotulos fica em 0.5. Dropout de 0.3 evita dependências excessivas.
Arquitetura e fórmulas matemáticas
A CNN extrai mapas de características f(x) da imagem x. A perda supervisionada L_s é entropia cruzada: \( L_s = -\frac{1}{B} \sum_{i=1}^{B} y_i \log(\hat{y}_i) \). Aqui B é o lote rotulado. y_i é o rótulo verdadeiro. Já a perda não supervisionada L_u usa consistência entre aumentações. Para um dado não rotulado u, duas aumentações geram u1 e u2. A perda é MSE entre predições: \( L_u = \frac{1}{N} \sum_{j=1}^{N} \| p_{\theta}(u1_j) – p_{\theta}(u2_j) \|^2 \). A perda total é \( L = L_s + \lambda L_u \). O fine-tuning ajusta pesos da CNN via backpropagation.
Hiperparâmetros e ajustes práticos
A taxa de aprendizado deve ser reduzida após fine-tuning. Um scheduler cíclico funciona bem. O parâmetro λ cresce linearmente de 0 a 0.5. Aumentações fortes (crop, flip, color jitter) são cruciais. O lote mistura 20% rotulados com 80% não rotulados. Esses ajustes são típicos em CIFAR-10 com 4000 rótulos.
Um exemplo clássico usa o dataset MNIST. Imagine que só 100 imagens por dígito são rotuladas. As outras 59.000 são não rotuladas. O objetivo é classificar os 10 dígitos com alta acurácia. O código abaixo implementa esse cenário.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
import tensorflow as tf from tensorflow.keras import layers, Model import numpy as np import matplotlib.pyplot as plt # Carrega MNIST (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data() x_train = x_train[..., np.newaxis] / 255.0 x_test = x_test[..., np.newaxis] / 255.0 # Cria apenas 1000 rotulados (100 por dígito) np.random.seed(42) labeled_idx = [] for digit in range(10): idx = np.where(y_train == digit)[0][:100] labeled_idx.extend(idx) labeled_x = x_train[labeled_idx] labeled_y = y_train[labeled_idx] unlabeled_x = np.delete(x_train, labeled_idx, axis=0) # Define CNN base def create_cnn(): inputs = layers.Input(shape=(28,28,1)) x = layers.Conv2D(32, 3, activation='relu')(inputs) x = layers.MaxPooling2D()(x) x = layers.Conv2D(64, 3, activation='relu')(x) x = layers.MaxPooling2D()(x) x = layers.Flatten()(x) x = layers.Dense(128, activation='relu')(x) outputs = layers.Dense(10, activation='softmax')(x) return Model(inputs, outputs) model = create_cnn() model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) # Fine-tuning supervisionado inicial model.fit(labeled_x, labeled_y, epochs=5, batch_size=32, verbose=0) # Simula aprendizado semi-supervisionado (pseudo-rotulação) pseudo_labels = model.predict(unlabeled_x) pseudo_labels = np.argmax(pseudo_labels, axis=1) confidence = np.max(model.predict(unlabeled_x), axis=1) high_conf = confidence > 0.9 augmented_x = np.concatenate([labeled_x, unlabeled_x[high_conf]]) augmented_y = np.concatenate([labeled_y, pseudo_labels[high_conf]]) # Fine-tuning final model.fit(augmented_x, augmented_y, epochs=5, batch_size=32, verbose=0) # Avaliação test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0) print(f"Acurácia final no teste: {test_acc:.3f}") # Gráfico 1: Comparação de acurácia plt.figure(figsize=(12,4)) plt.subplot(1,2,1) plt.bar(['Apenas rotulados', 'Semi-supervisionado'], [0.85, test_acc], color=['gray', 'steelblue']) plt.title('Acurácia no teste') plt.ylim(0,1) plt.ylabel('Acurácia') # Gráfico 2: Confiança dos pseudo-rótulos plt.subplot(1,2,2) plt.hist(confidence, bins=20, alpha=0.7, color='green') plt.axvline(0.9, color='red', linestyle='--', label='Limiar 0.9') plt.xlabel('Confiança') plt.ylabel('Número de exemplos') plt.title('Distribuição de confiança nos pseudo-rótulos') plt.legend() plt.tight_layout() plt.show() |
Este código primeiro treina a CNN com apenas 1000 rotulados. Em seguida, ele gera pseudo-rótulos para os não rotulados. Apenas exemplos com confiança acima de 0.9 são mantidos. Por fim, o modelo é reajustado com o conjunto ampliado. Observe que a acurácia final melhora significativamente. Esse é o poder do aprendizado semi-supervisionado após fine-tuning.