Machine learning / Feature engineering and preprocessing
Diving Deeper into Feature Scaling in Machine Learning with Python
Contents
- Introduction to Feature Scaling
- Why Feature Scaling Matters
- Common Feature Scaling Techniques
- Standardization (Z-score Normalization)
- Min-Max Scaling
- Max Abs Scaling
- Robust Scaling
- Handling Outliers in Feature Scaling
- Feature Scaling for Time Series Data
- Feature Scaling for Categorical Variables
- Feature Scaling in Neural Networks
- Real-life Example: Image Processing
- Real-life Example: Natural Language Processing
- Choosing the Right Scaling Technique
Introduction to Feature Scaling
Feature scaling is a crucial preprocessing step in machine learning that transforms the features of a dataset to a common scale. This process ensures that all features contribute equally to the model's performance, preventing features with larger magnitudes from dominating those with smaller magnitudes.
import numpy as np
import matplotlib.pyplot as plt
# Generate sample data
np.random.seed(42)
feature1 = np.random.uniform(0, 100, 100)
feature2 = np.random.uniform(0, 1, 100)
# Plot original data
plt.figure(figsize=(10, 5))
plt.subplot(121)
plt.scatter(feature1, feature2)
plt.title('Original Data')
plt.xlabel('Feature 1 (0-100 range)')
plt.ylabel('Feature 2 (0-1 range)')
# Display the plot
plt.tight_layout()
plt.show()
Why Feature Scaling Matters
Feature scaling is essential because many machine learning algorithms are sensitive to the scale of input features. Algorithms that rely on distance calculations, such as k-Nearest Neighbors (k-NN) or Support Vector Machines (SVM), can be particularly affected by unscaled features, leading to biased results and suboptimal model performance.
from sklearn.preprocessing import StandardScaler
# Create a StandardScaler object
scaler = StandardScaler()
# Fit the scaler to the data and transform it
scaled_data = scaler.fit_transform(np.column_stack((feature1, feature2)))
# Plot scaled data
plt.subplot(122)
plt.scatter(scaled_data[:, 0], scaled_data[:, 1])
plt.title('Scaled Data')
plt.xlabel('Feature 1 (scaled)')
plt.ylabel('Feature 2 (scaled)')
# Display the plot
plt.tight_layout()
plt.show()
Common Feature Scaling Techniques
There are several feature scaling techniques, each with its own characteristics and use cases. The most common methods include:
- Standardization (Z-score normalization)
- Min-Max Scaling
- Max Abs Scaling
- Robust Scaling
We'll explore each of these techniques in the following slides.
from sklearn.preprocessing import StandardScaler, MinMaxScaler, MaxAbsScaler, RobustScaler
# Generate sample data
np.random.seed(42)
data = np.random.randn(100, 1) * 20 + 50
# Initialize scalers
scalers = [
StandardScaler(),
MinMaxScaler(),
MaxAbsScaler(),
RobustScaler()
]
# Plot original and scaled data
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
fig.suptitle('Comparison of Scaling Techniques')
axes[0, 0].hist(data, bins=20)
axes[0, 0].set_title('Original Data')
for i, scaler in enumerate(scalers):
scaled_data = scaler.fit_transform(data)
axes[(i+1)//3, (i+1)%3].hist(scaled_data, bins=20)
axes[(i+1)//3, (i+1)%3].set_title(type(scaler).__name__)
plt.tight_layout()
plt.show()
Standardization (Z-score Normalization)
Standardization transforms features to have zero mean and unit variance. This technique is widely used and works well for many machine learning algorithms, especially when the data follows a Gaussian distribution.
from sklearn.preprocessing import StandardScaler
import numpy as np
# Sample data
data = np.array([1, 5, 3, 7, 9, 2, 4, 6, 8]).reshape(-1, 1)
# Create and apply StandardScaler
scaler = StandardScaler()
standardized_data = scaler.fit_transform(data)
print("Original data:", data.ravel())
print("Standardized data:", standardized_data.ravel())
print("Mean:", standardized_data.mean())
print("Standard deviation:", standardized_data.std())
Min-Max Scaling
Min-Max scaling transforms features to a fixed range, typically between 0 and 1. This method is useful when you need bounded values and want to preserve zero entries in sparse data.
from sklearn.preprocessing import MinMaxScaler
import numpy as np
# Sample data
data = np.array([1, 5, 3, 7, 9, 2, 4, 6, 8]).reshape(-1, 1)
# Create and apply MinMaxScaler
scaler = MinMaxScaler()
minmax_scaled_data = scaler.fit_transform(data)
print("Original data:", data.ravel())
print("Min-Max scaled data:", minmax_scaled_data.ravel())
print("Min value:", minmax_scaled_data.min())
print("Max value:", minmax_scaled_data.max())
Max Abs Scaling
Max Abs scaling scales each feature by its maximum absolute value. This technique is useful when dealing with sparse data and when you want to maintain the sign of the input features.
from sklearn.preprocessing import MaxAbsScaler
import numpy as np
# Sample data
data = np.array([-4, 1, -9, 2, 7, -5, 3, -1, 8]).reshape(-1, 1)
# Create and apply MaxAbsScaler
scaler = MaxAbsScaler()
maxabs_scaled_data = scaler.fit_transform(data)
print("Original data:", data.ravel())
print("Max Abs scaled data:", maxabs_scaled_data.ravel())
print("Max absolute value:", np.abs(maxabs_scaled_data).max())
Robust Scaling
Robust scaling uses statistics that are robust to outliers. It scales features using the interquartile range (IQR) and is less affected by extreme values compared to other scaling methods.
from sklearn.preprocessing import RobustScaler
import numpy as np
# Sample data with outliers
data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 100]).reshape(-1, 1)
# Create and apply RobustScaler
scaler = RobustScaler()
robust_scaled_data = scaler.fit_transform(data)
print("Original data:", data.ravel())
print("Robust scaled data:", robust_scaled_data.ravel())
Handling Outliers in Feature Scaling
Outliers can significantly impact the effectiveness of feature scaling. Robust scaling and winsorization are two techniques that can help mitigate the effect of outliers on your scaled features.
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
# Generate data with outliers
np.random.seed(42)
data = np.concatenate([np.random.normal(0, 1, 100), np.random.normal(10, 1, 5)])
# Perform winsorization
winsorized_data = stats.mstats.winsorize(data, limits=[0.05, 0.05])
# Plot original and winsorized data
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
ax1.boxplot(data)
ax1.set_title('Original Data')
ax2.boxplot(winsorized_data)
ax2.set_title('Winsorized Data')
plt.show()
print("Original data statistics:")
print(stats.describe(data))
print("\nWinsorized data statistics:")
print(stats.describe(winsorized_data))
Feature Scaling for Time Series Data
When dealing with time series data, it's important to consider the temporal nature of the features. One approach is to use a rolling window for scaling to preserve the time-dependent structure of the data.
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
# Generate sample time series data
np.random.seed(42)
dates = pd.date_range(start='2023-01-01', periods=100, freq='D')
values = np.random.randn(100).cumsum()
df = pd.DataFrame({'date': dates, 'value': values})
# Function to scale data using a rolling window
def rolling_scale(data, window_size):
scaler = StandardScaler()
scaled_data = []
for i in range(len(data)):
window = data.iloc[max(0, i-window_size+1):i+1]
scaled_value = scaler.fit_transform(window.values.reshape(-1, 1))[-1][0]
scaled_data.append(scaled_value)
return scaled_data
# Apply rolling window scaling
df['scaled_value'] = rolling_scale(df['value'], window_size=30)
# Plot original and scaled data
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10))
ax1.plot(df['date'], df['value'])
ax1.set_title('Original Time Series')
ax2.plot(df['date'], df['scaled_value'])
ax2.set_title('Scaled Time Series (30-day rolling window)')
plt.tight_layout()
plt.show()
Feature Scaling for Categorical Variables
While feature scaling is typically applied to numerical variables, categorical variables can also benefit from scaling techniques, especially when used with certain algorithms. One common approach is to use one-hot encoding followed by scaling.
import pandas as pd
from sklearn.preprocessing import OneHotEncoder, StandardScaler
# Sample categorical data
data = pd.DataFrame({
'color': ['red', 'blue', 'green', 'red', 'blue'],
'size': ['small', 'medium', 'large', 'medium', 'small']
})
# One-hot encode categorical variables
encoder = OneHotEncoder(sparse=False)
encoded_data = encoder.fit_transform(data)
# Scale the encoded data
scaler = StandardScaler()
scaled_data = scaler.fit_transform(encoded_data)
# Create a DataFrame with scaled values
scaled_df = pd.DataFrame(scaled_data, columns=encoder.get_feature_names(['color', 'size']))
print("Original data:")
print(data)
print("\nScaled one-hot encoded data:")
print(scaled_df)
Feature Scaling in Neural Networks
In neural networks, feature scaling is crucial for faster convergence and improved model performance. Different scaling techniques can be applied depending on the activation functions used in the network.
import numpy as np
import tensorflow as tf
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Generate sample data
np.random.seed(42)
X = np.random.randn(1000, 5)
y = np.random.randint(0, 2, 1000)
# Create and compile models
def create_model(input_shape):
model = tf.keras.Sequential([
tf.keras.layers.Dense(64, activation='relu', input_shape=input_shape),
tf.keras.layers.Dense(32, activation='relu'),
tf.keras.layers.Dense(1, activation='sigmoid')
])
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
return model
# Train models with different scaling techniques
scalers = [StandardScaler(), MinMaxScaler()]
histories = []
for scaler in scalers:
X_scaled = scaler.fit_transform(X)
model = create_model((X.shape[1],))
history = model.fit(X_scaled, y, epochs=50, validation_split=0.2, verbose=0)
histories.append(history)
# Plot training curves
plt.figure(figsize=(12, 5))
plt.subplot(121)
plt.plot(histories[0].history['loss'], label='StandardScaler')
plt.plot(histories[1].history['loss'], label='MinMaxScaler')
plt.title('Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.subplot(122)
plt.plot(histories[0].history['accuracy'], label='StandardScaler')
plt.plot(histories[1].history['accuracy'], label='MinMaxScaler')
plt.title('Training Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.tight_layout()
plt.show()
Real-life Example: Image Processing
In image processing, feature scaling is often used to normalize pixel values. This is particularly important when working with neural networks for tasks like image classification or object detection.
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
# Load and display an image
image_path = "path/to/your/image.jpg" # Replace with an actual image path
image = Image.open(image_path)
image_array = np.array(image)
# Display original image
plt.figure(figsize=(12, 4))
plt.subplot(131)
plt.imshow(image_array)
plt.title('Original Image')
# Scale image to [0, 1] range
scaled_image = image_array / 255.0
plt.subplot(132)
plt.imshow(scaled_image)
plt.title('Scaled Image [0, 1]')
# Scale image to [-1, 1] range
scaled_image_centered = (image_array / 127.5) - 1
plt.subplot(133)
plt.imshow(scaled_image_centered, cmap='gray')
plt.title('Scaled Image [-1, 1]')
plt.tight_layout()
plt.show()
print("Original image data range:", image_array.min(), "-", image_array.max())
print("Scaled image data range [0, 1]:", scaled_image.min(), "-", scaled_image.max())
print("Scaled image data range [-1, 1]:", scaled_image_centered.min(), "-", scaled_image_centered.max())
Real-life Example: Natural Language Processing
In natural language processing, feature scaling is often applied to word embeddings or document vectors. This example demonstrates how to scale TF-IDF vectors for improved text classification.
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.preprocessing import StandardScaler
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
# Sample text data
texts = [
"The quick brown fox jumps over the lazy dog",
"A journey of a thousand miles begins with a single step",
"To be or not to be, that is the question",
"All that glitters is not gold",
"The early bird catches the worm"
]
labels = [0, 1, 1, 0, 1]
# Split data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(texts, labels, test_size=0.2, random_state=42)
# Create a pipeline with TF-IDF, scaling, and Naive Bayes classifier
pipeline = Pipeline([
('tfidf', TfidfVectorizer()),
('scaler', StandardScaler(with_mean=False)), # StandardScaler for sparse data
('clf', MultinomialNB())
])
# Fit the pipeline and make predictions
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
# Print classification report
print(classification_report(y_test, y_pred))
Choosing the Right Scaling Technique
Selecting the appropriate scaling technique depends on various factors, including the nature of your data, the machine learning algorithm you're using, and the specific requirements of your problem. Here's a guide to help you choose:
- Use StandardScaler when your data is roughly normally distributed and you're using algorithms sensitive to feature magnitudes (e.g., neural networks, SVM).
- Apply MinMaxScaler when you need bounded values or working with algorithms that require non-negative inputs (e.g., neural networks with sigmoid output).
- Opt for MaxAbsScaler when dealing with sparse data or when you want to preserve zero entries.
- Choose RobustScaler when your data contains many outliers, and you want to minimize their influence.
- Consider not scaling for tree-based models (e.