Antes de tudo, feliz Ano Novo! Agradecemos à plataforma CSDN por ser um espaço acolhedor para troca técnica. Neste Ano da Serpente, dseejamos crescimento contínuo, eventos vibrantes e que mais desenvolvedores superem barreiras técnicas, explorando todo o potencial no novo ano.
O que é Normalização de Dados?
Durante a preparação de dados, escalonar características — como normalização ou padronização — é essencial. Métricas distintas frequentemente possuem unidades e mangitudes diferentes, o que pode distorcer análises. Para tornar os dados comparáveis e eliminar viés dimensional, aplicamos transformações que ajustam todos os valores a uma mesma escala, facilitando comparações e melhorando a qualidade das decisões baseadas em dados.
Vantagens da Normalização
- Convergência mais rápida em modelos de aprendizado: Quando variáveis estão em escalas muito diferentes, superfícies de perda se tornam alongadas, dificultando a descida do gradiente. Normalizar equaliza as escalas, resultando em caminhos de otimização mais curtos e estáveis.
- Remoção de unidades físicas (adimensionalização): Transforma dados em valores relativos entre 0 e 1, independentemente da unidade original — útil para comparação direta entre atributos heterogêneos.
Exemplo Visual: Superfície de Perda sem e com Normalização
import numpy as np
import matplotlib.pyplot as plt
# Gera dados sintéticos com escalas diferentes
feat_a = np.random.rand(100) * 10 # Pequena escala
feat_b = np.random.rand(100) * 1000 # Grande escala
target = 15 + 20*feat_a + 0.1*feat_b + np.random.rand(100)*5
# Monta matriz de design
X_raw = np.column_stack([np.ones(100), feat_a, feat_b])
# Função de custo MSE
def compute_mse(weights, X, y_true):
preds = X @ weights
return np.mean((y_true - preds)**2) / 2
# Espaço de parâmetros para plotagem
w_range_1 = np.linspace(-10, 50, 80)
w_range_2 = np.linspace(-0.3, 0.5, 80)
W1, W2 = np.meshgrid(w_range_1, w_range_2)
loss_surface = np.zeros_like(W1)
for i in range(W1.shape[0]):
for j in range(W1.shape[1]):
w_vec = np.array([0, W1[i,j], W2[i,j]])
loss_surface[i,j] = compute_mse(w_vec, X_raw, target)
plt.figure(figsize=(7,5))
plt.contour(W1, W2, loss_surface, levels=40, cmap='plasma')
plt.colorbar(label='Perda MSE')
plt.xlabel('Peso W₁')
plt.ylabel('Peso W₂')
plt.title('Superfície de Perda (Dados Originais)')
plt.show()
Ao normalizar as entradas usando Min-Max, os pesos convergem em intervalos similares, evitando problemas numéricos e oscilações indesejadas:
# Normalização Min-Max
norm_func = lambda x: (x - x.min()) / (x.max() - x.min())
feat_a_norm = norm_func(feat_a) * 100
feat_b_norm = norm_func(feat_b) * 100
X_norm = np.column_stack([np.ones(100), feat_a_norm, feat_b_norm])
# Recalcula superfície com dados normalizados
loss_norm = np.zeros_like(W1)
w_grid_small = np.linspace(-2, 5, 80)
W1s, W2s = np.meshgrid(w_grid_small, w_grid_small)
for i in range(W1s.shape[0]):
for j in range(W1s.shape[1]):
w_vec = np.array([0, W1s[i,j], W2s[i,j]])
loss_norm[i,j] = compute_mse(w_vec, X_norm, target)
plt.figure(figsize=(7,5))
plt.contour(W1s, W2s, loss_norm, levels=40, cmap='inferno')
plt.colorbar(label='Perda MSE')
plt.xlabel('Peso W₁ (normalizado)')
plt.ylabel('Peso W₂ (normalizado)')
plt.title('Superfície de Perda (Dados Normalizados)')
plt.show()
Métodos Comuns de Normalização
1. Normalização Min-Max
Transforma dados para o intervalo [0,1]:
x_scaled = (x - x_min) / (x_max - x_min)
Para mapear para outro intervalo [a,b]:
x_scaled = a + (x - x_min) * (b - a) / (x_max - x_min)
2. Padronização Z-Score
Centraliza em média zero e desvio padrão unitário:
z = (x - μ) / σ
def z_score_transform(arr):
mean_val = np.mean(arr)
std_val = np.std(arr)
return (arr - mean_val) / std_val
sample = np.array([10, 20, 30, 40, 50])
print("Original:", sample)
print("Z-Score:", z_score_transform(sample))
3. Escalonamento Decimal
Move a vírgula decimal até que todos os valores fiquem entre [-1,1]:
def decimal_scale(data):
max_abs = np.max(np.abs(data))
scale_power = len(str(int(max_abs)))
divisor = 10 ** scale_power
return data / divisor
values = np.array([1234, -5678, 91011])
print("Original:", values)
print("Escalonado:", decimal_scale(values))
4. RobustScaler (Baseado em Mediana e IQR)
Resistente a outliers, usa quartis em vez de média/desvio:
x_scaled = (x - mediana) / (Q3 - Q1)
def robust_transform(data, q_low=25, q_high=75):
q25 = np.percentile(data, q_low)
q75 = np.percentile(data, q_high)
median = np.median(data)
iqr = q75 - q25
if iqr == 0:
iqr = 1 # Evita divisão por zero
return (data - median) / iqr
noisy_data = np.array([1, 2, 3, 4, 1000]) # Com outlier
print("Original:", noisy_data)
print("RobustScaler:", robust_transform(noisy_data))
Implementação com Bibliotecas
O scikit-learn oferece escalonadores prontos:
from sklearn.preprocessing import MinMaxScaler, StandardScaler, RobustScaler
data = np.random.rand(100, 1) * 500
scalers = {
'MinMax': MinMaxScaler(),
'Standard': StandardScaler(),
'Robust': RobustScaler()
}
fig, axes = plt.subplots(1, 4, figsize=(14, 3))
axes[0].hist(data, bins=20, color='gray', alpha=0.7)
axes[0].set_title('Original')
for idx, (name, scaler) in enumerate(scalers.items(), 1):
scaled = scaler.fit_transform(data)
axes[idx].hist(scaled, bins=20, color='steelblue', alpha=0.7)
axes[idx].set_title(name)
plt.tight_layout()
plt.show()