Chuyển đến nội dung chính

Từ 1D đến N-Chiều: Hiểu về Không gian Dữ liệu và Normalization

Bạn có bao giờ thắc mắc tại sao khi học Machine Learning, người ta luôn nhấn mạnh về "normalization" (chuẩn hóa dữ liệu)? Và tại sao khi code, nhiều người bị "kẹt" ở mảng 2D, 3D trong C# hay các ngôn ngữ khác mà không biết cách tư duy về không gian nhiều chiều hơn?

Bài viết này sẽ giúp bạn hiểu rõ vấn đề thông qua một ví dụ đơn giản nhưng rất trực quan.

Bài toán: Dự đoán người mua nhà

Giả sử chúng ta muốn dự đoán một người có mua nhà hay không dựa trên:

  • Chiều cao (Height): từ 165cm đến 180cm
  • Thu nhập (Income): từ 8 triệu đến 20 triệu VNĐ/tháng

Lưu ý: Ví dụ này không nhằm hợp lý về mặt kinh tế (chiều cao không liên quan đến việc mua nhà), mà chỉ để minh họa tác động của normalization lên không gian dữ liệu.

Dataset mẫu

Height,Income,BuyHouse
165,8000000,false
168,9000000,false
170,10000000,false
172,12000000,false
175,15000000,true
178,18000000,true
180,20000000,true

Phần 1: Không gian 1 Chiều (1D)

Giả sử ban đầu chúng ta chỉ xét một yếu tố duy nhất: Thu nhập.

Code Python trong Google Colab

# Cell 1: Import thư viện
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn.preprocessing import StandardScaler, MinMaxScaler

%matplotlib inline

print("✓ Import thành công!")
# Cell 2: Tạo dataset
data = {
    'Height': [165, 168, 170, 172, 175, 178, 180],
    'Income': [8000000, 9000000, 10000000, 12000000, 15000000, 18000000, 20000000],
    'BuyHouse': [False, False, False, False, True, True, True]
}

df = pd.DataFrame(data)
print("Dataset gốc:")
print(df)
Đối chiếu với .NET
| Python          | .NET           |
| --------------- | -------------- |
| DataFrame       | DataTable      |
| df["Age"]       | row["Age"]     |
| df.head()       | SELECT TOP     |
| df[df.Age > 20] | WHERE Age > 20 |
# Cell 3: Phân tích không gian 1D - chỉ xét Thu nhập
plt.figure(figsize=(12, 4))

# Vẽ trên trục số
plt.subplot(1, 2, 1)
colors = ['red' if not buy else 'green' for buy in df['BuyHouse']]
plt.scatter(df['Income'], [0]*len(df), c=colors, s=200, alpha=0.6)
plt.xlabel('Thu nhập (VNĐ)', fontsize=12)
plt.yticks([])
plt.title('Không gian 1D: Chỉ có Thu nhập', fontsize=14, fontweight='bold')
plt.axvline(x=13000000, color='blue', linestyle='--', linewidth=2, label='Ngưỡng quyết định')
plt.legend()
plt.grid(True, alpha=0.3)

# Vẽ dạng bar chart
plt.subplot(1, 2, 2)
plt.bar(range(len(df)), df['Income'], color=colors, alpha=0.6)
plt.xlabel('Người', fontsize=12)
plt.ylabel('Thu nhập (VNĐ)', fontsize=12)
plt.title('Thu nhập từng người', fontsize=14, fontweight='bold')
plt.axhline(y=13000000, color='blue', linestyle='--', linewidth=2, label='Ngưỡng')
plt.legend()

plt.tight_layout()
plt.show()

print("Đỏ = Không mua nhà | Xanh = Mua nhà")
print("Trong không gian 1D, chúng ta chỉ có 1 trục duy nhất để phân loại.")

Nhận xét về 1D

  • Dễ visualize (chỉ là một đường thẳng)
  • Dễ tìm ngưỡng quyết định (threshold)
  • Nhưng thiếu thông tin (bỏ qua chiều cao)

Phần 2: Không gian 2 Chiều (2D) - CHƯA Normalization

Bây giờ chúng ta xét cả hai yếu tố: Chiều cao và Thu nhập.

# Cell 4: Vẽ không gian 2D - CHƯA chuẩn hóa
plt.figure(figsize=(14, 6))

# Biểu đồ 1: Dữ liệu gốc
plt.subplot(1, 2, 1)
for i, row in df.iterrows():
    color = 'red' if not row['BuyHouse'] else 'green'
    plt.scatter(row['Height'], row['Income'], c=color, s=150, alpha=0.7)
    plt.text(row['Height']+0.5, row['Income']+200000, f"P{i+1}", fontsize=9)

plt.xlabel('Chiều cao (cm)', fontsize=12)
plt.ylabel('Thu nhập (VNĐ)', fontsize=12)
plt.title('Không gian 2D - CHƯA Normalization', fontsize=14, fontweight='bold')
plt.grid(True, alpha=0.3)

# Biểu đồ 2: Zoom vào để thấy vấn đề
plt.subplot(1, 2, 2)
for i, row in df.iterrows():
    color = 'red' if not row['BuyHouse'] else 'green'
    plt.scatter(row['Height'], row['Income'], c=color, s=150, alpha=0.7)

plt.xlabel('Chiều cao (cm)', fontsize=12)
plt.ylabel('Thu nhập (VNĐ)', fontsize=12)
plt.title('Nhìn kỹ hơn - Thấy vấn đề gì không?', fontsize=14, fontweight='bold')
plt.xlim(160, 185)
plt.ylim(0, 25000000)
plt.grid(True, alpha=0.3)

# Vẽ tỷ lệ scale
plt.axhline(y=20000000, xmin=0.7, xmax=0.9, color='orange', linewidth=3)
plt.text(175, 21000000, '← Thu nhập: 20M', fontsize=10, color='orange')
plt.axvline(x=180, ymin=0.7, ymax=0.9, color='purple', linewidth=3)
plt.text(181, 15000000, 'Chiều cao:\n15cm →', fontsize=10, color='purple')

plt.tight_layout()
plt.show()

print("\n⚠️ VẤN ĐỀ:")
print(f"Chiều cao: {df['Height'].min()}-{df['Height'].max()} (khoảng {df['Height'].max()-df['Height'].min()} đơn vị)")
print(f"Thu nhập: {df['Income'].min():,}-{df['Income'].max():,} (khoảng {df['Income'].max()-df['Income'].min():,} đơn vị)")
print(f"\nTỷ lệ: Thu nhập lớn hơn Chiều cao {(df['Income'].max()-df['Income'].min())/(df['Height'].max()-df['Height'].min()):,.0f} lần!")


Vấn đề của dữ liệu CHƯA chuẩn hóa

Hãy tính khoảng cách giữa 2 điểm trong không gian 2D:

# Cell 5: Tính khoảng cách Euclidean
def euclidean_distance(p1, p2):
    return np.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2)

# Người 1 vs Người 5
p1 = [df.loc[0, 'Height'], df.loc[0, 'Income']]  # [165, 8000000]
p5 = [df.loc[4, 'Height'], df.loc[4, 'Income']]  # [175, 15000000]

distance = euclidean_distance(p1, p5)

print("Khoảng cách giữa Người 1 và Người 5:")
print(f"Δ Chiều cao: {p5[0]-p1[0]} cm")
print(f"Δ Thu nhập: {p5[1]-p1[1]:,} VNĐ")
print(f"Khoảng cách Euclidean: {distance:,.2f}")
print(f"\n⚠️ Khoảng cách bị chi phối gần như HOÀN TOÀN bởi Thu nhập!")
print(f"   Chiều cao hầu như KHÔNG ảnh hưởng: {((p5[0]-p1[0])**2 / distance**2 * 100):.6f}%")

Kết quả: Khoảng cách ~7,000,007. Trong đó:

  • Chiều cao đóng góp: 10² = 100
  • Thu nhập đóng góp: 7,000,000² = 49,000,000,000,000

→ Chiều cao hầu như KHÔNG đóng góp gì!

Phần 3: Normalization - Giải pháp

Phương pháp 1: Min-Max Scaling (0-1)

Công thức: x_scaled = (x - min) / (max - min)

# Cell 6: Min-Max Normalization
scaler_minmax = MinMaxScaler()
df_minmax = df.copy()
df_minmax[['Height', 'Income']] = scaler_minmax.fit_transform(df[['Height', 'Income']])

print("Sau khi Min-Max Scaling (0-1):")
print(df_minmax)
Kết quả
Sau khi Min-Max Scaling (0-1):
     Height    Income  BuyHouse
0  0.000000  0.000000     False
1  0.200000  0.083333     False
2  0.333333  0.166667     False
3  0.466667  0.333333     False
4  0.666667  0.583333      True
5  0.866667  0.833333      True
6  1.000000  1.000000      True

Phương pháp 2: Standardization (Z-score)

Công thức: x_scaled = (x - mean) / std

# Cell 7: Standardization
scaler_standard = StandardScaler()
df_standard = df.copy()
df_standard[['Height', 'Income']] = scaler_standard.fit_transform(df[['Height', 'Income']])

print("Sau khi Standardization (Z-score):")
print(df_standard)
Sau khi Standardization (Z-score):
     Height    Income  BuyHouse
0 -1.511205 -1.198669     False
1 -0.912426 -0.965594     False
2 -0.513239 -0.732520     False
3 -0.114053 -0.266371     False
4  0.484726  0.432853      True
5  1.083505  1.132076      True
6  1.482691  1.598225      True

So sánh trực quan

# Cell 8: So sánh 3 trường hợp
fig, axes = plt.subplots(1, 3, figsize=(18, 5))

datasets = [
    (df, 'Dữ liệu GỐC (CHƯA chuẩn hóa)', 'Height', 'Income'),
    (df_minmax, 'Min-Max Scaling (0-1)', 'Height', 'Income'),
    (df_standard, 'Standardization (Z-score)', 'Height', 'Income')
]

for idx, (data, title, xcol, ycol) in enumerate(datasets):
    ax = axes[idx]
    
    for i, row in data.iterrows():
        color = 'red' if not row['BuyHouse'] else 'green'
        ax.scatter(row[xcol], row[ycol], c=color, s=150, alpha=0.7)
        ax.text(row[xcol]+0.01, row[ycol]+0.01, f"P{i+1}", fontsize=8)
    
    ax.set_xlabel(xcol, fontsize=11)
    ax.set_ylabel(ycol, fontsize=11)
    ax.set_title(title, fontsize=13, fontweight='bold')
    ax.grid(True, alpha=0.3)
    ax.set_aspect('equal', adjustable='box')

plt.tight_layout()
plt.show()

print("Đỏ = Không mua nhà | Xanh = Mua nhà")
print("\nChú ý: Sau khi chuẩn hóa, CẢ HAI trục có cùng tỷ lệ ảnh hưởng!")


Tính lại khoảng cách sau normalization

# Cell 9: So sánh khoảng cách
p1_original = [df.loc[0, 'Height'], df.loc[0, 'Income']]
p5_original = [df.loc[4, 'Height'], df.loc[4, 'Income']]

p1_minmax = [df_minmax.loc[0, 'Height'], df_minmax.loc[0, 'Income']]
p5_minmax = [df_minmax.loc[4, 'Height'], df_minmax.loc[4, 'Income']]

p1_standard = [df_standard.loc[0, 'Height'], df_standard.loc[0, 'Income']]
p5_standard = [df_standard.loc[4, 'Height'], df_standard.loc[4, 'Income']]

dist_original = euclidean_distance(p1_original, p5_original)
dist_minmax = euclidean_distance(p1_minmax, p5_minmax)
dist_standard = euclidean_distance(p1_standard, p5_standard)

print("KHOẢNG CÁCH GIỮA NGƯỜI 1 VÀ NGƯỜI 5:")
print("="*60)
print(f"Dữ liệu gốc:        {dist_original:,.2f}")
print(f"  → Chiều cao đóng góp: {((p5_original[0]-p1_original[0])**2 / dist_original**2 * 100):.6f}%")
print(f"  → Thu nhập đóng góp:  {((p5_original[1]-p1_original[1])**2 / dist_original**2 * 100):.6f}%")
print()
print(f"Min-Max (0-1):      {dist_minmax:.4f}")
print(f"  → Chiều cao đóng góp: {((p5_minmax[0]-p1_minmax[0])**2 / dist_minmax**2 * 100):.2f}%")
print(f"  → Thu nhập đóng góp:  {((p5_minmax[1]-p1_minmax[1])**2 / dist_minmax**2 * 100):.2f}%")
print()
print(f"Standardization:    {dist_standard:.4f}")
print(f"  → Chiều cao đóng góp: {((p5_standard[0]-p1_standard[0])**2 / dist_standard**2 * 100):.2f}%")
print(f"  → Thu nhập đóng góp:  {((p5_standard[1]-p1_standard[1])**2 / dist_standard**2 * 100):.2f}%")
print()
print("✅ Sau normalization, CẢ HAI chiều đều đóng góp vào khoảng cách!")
KHOẢNG CÁCH GIỮA NGƯỜI 1 VÀ NGƯỜI 5:
============================================================
Dữ liệu gốc:        7,000,000.00
  → Chiều cao đóng góp: 0.000000%
  → Thu nhập đóng góp:  100.000000%

Min-Max (0-1):      0.8858
  → Chiều cao đóng góp: 56.64%
  → Thu nhập đóng góp:  43.36%

Standardization:    2.5779
  → Chiều cao đóng góp: 59.95%
  → Thu nhập đóng góp:  40.05%

✅ Sau normalization, CẢ HAI chiều đều đóng góp vào khoảng cách!

Phần 4: Không gian 3 Chiều (3D)

Giả sử chúng ta thêm một yếu tố thứ 3: Tuổi

# Cell 10: Thêm chiều thứ 3 - Tuổi
df['Age'] = [25, 28, 30, 32, 35, 38, 40]

print("Dataset với 3 chiều:")
print(df)

# Chuẩn hóa 3 chiều
df_3d_norm = df.copy()
df_3d_norm[['Height', 'Income', 'Age']] = StandardScaler().fit_transform(
    df[['Height', 'Income', 'Age']]
)

print("\nSau khi chuẩn hóa:")
print(df_3d_norm[['Height', 'Income', 'Age', 'BuyHouse']])
Kết quả
Dataset với 3 chiều:
   Height    Income  BuyHouse  Age
0     165   8000000     False   25
1     168   9000000     False   28
2     170  10000000     False   30
3     172  12000000     False   32
4     175  15000000      True   35
5     178  18000000      True   38
6     180  20000000      True   40

Sau khi chuẩn hóa:
     Height    Income       Age  BuyHouse
0 -1.511205 -1.198669 -1.511205     False
1 -0.912426 -0.965594 -0.912426     False
2 -0.513239 -0.732520 -0.513239     False
3 -0.114053 -0.266371 -0.114053     False
4  0.484726  0.432853  0.484726      True
5  1.083505  1.132076  1.083505      True
6  1.482691  1.598225  1.482691      True
Vẽ biểu đồ 3D
# Cell 11: Vẽ biểu đồ 3D
fig = plt.figure(figsize=(16, 6))

# Biểu đồ 1: Dữ liệu gốc (3D)
ax1 = fig.add_subplot(1, 2, 1, projection='3d')
for i, row in df.iterrows():
    color = 'red' if not row['BuyHouse'] else 'green'
    ax1.scatter(row['Height'], row['Income'], row['Age'], 
                c=color, s=150, alpha=0.7)

ax1.set_xlabel('Chiều cao (cm)', fontsize=10)
ax1.set_ylabel('Thu nhập (VNĐ)', fontsize=10)
ax1.set_zlabel('Tuổi', fontsize=10)
ax1.set_title('3D - Dữ liệu GỐC (CHƯA chuẩn hóa)', fontsize=13, fontweight='bold')

# Biểu đồ 2: Dữ liệu đã chuẩn hóa (3D)
ax2 = fig.add_subplot(1, 2, 2, projection='3d')
for i, row in df_3d_norm.iterrows():
    color = 'red' if not row['BuyHouse'] else 'green'
    ax2.scatter(row['Height'], row['Income'], row['Age'], 
                c=color, s=150, alpha=0.7)

ax2.set_xlabel('Chiều cao (chuẩn hóa)', fontsize=10)
ax2.set_ylabel('Thu nhập (chuẩn hóa)', fontsize=10)
ax2.set_zlabel('Tuổi (chuẩn hóa)', fontsize=10)
ax2.set_title('3D - Sau Standardization', fontsize=13, fontweight='bold')

# Set aspect ratio cho biểu đồ 2
ax2.set_box_aspect([1,1,1])

plt.tight_layout()
plt.show()

print("Trong không gian 3D, dữ liệu chuẩn hóa giúp các chiều cân bằng với nhau.")


Phần 5: Tư duy về N-Chiều (Beyond 3D)

Đây là phần quan trọng nhất: Làm sao thoát khỏi tư duy mảng 2D, 3D?

Vấn đề khi code C# (hoặc bất kỳ ngôn ngữ nào)

Nhiều người bị "kẹt" vì nghĩ:

// C# - Tư duy SAI
int[] array1D = new int[10];           // 1 chiều
int[,] array2D = new int[10, 10];      // 2 chiều
int[,,] array3D = new int[10, 10, 10]; // 3 chiều
// Còn 4 chiều, 5 chiều thì sao??? ❌

Giải pháp: Tư duy về VECTOR và MATRIX

Thay vì nghĩ về "mảng nhiều chiều", hãy nghĩ về:

  • Mỗi điểm dữ liệu = 1 vector
  • Toàn bộ dataset = 1 matrix (mảng 2D) với shape (n_samples, n_features)
# Cell 12: Tư duy đúng về N-chiều
print("TƯ DUY VỀ N-CHIỀU")
print("="*70)

# Ví dụ với 2 chiều
print("\n1. Dataset 2 chiều (Height, Income):")
print(f"   Shape: {df[['Height', 'Income']].values.shape}")
print(f"   → (7 người, 2 features)")
print(df[['Height', 'Income']].values)

# Ví dụ với 3 chiều
print("\n2. Dataset 3 chiều (Height, Income, Age):")
print(f"   Shape: {df[['Height', 'Income', 'Age']].values.shape}")
print(f"   → (7 người, 3 features)")
print(df[['Height', 'Income', 'Age']].values)

# Ví dụ với 10 chiều
print("\n3. Giả sử có 10 features:")
# Tạo thêm 7 features giả
for i in range(1, 8):
    df[f'Feature{i}'] = np.random.rand(7)

all_features = [col for col in df.columns if col != 'BuyHouse']
print(f"   Shape: {df[all_features].values.shape}")
print(f"   → (7 người, {len(all_features)} features)")
print(f"\n   Đây vẫn chỉ là MẢNG 2D trong code!")
print(f"   Nhưng biểu diễn dữ liệu {len(all_features)}-chiều trong không gian toán học!")
Kết quả
TƯ DUY VỀ N-CHIỀU
======================================================================

1. Dataset 2 chiều (Height, Income):
   Shape: (7, 2)
   → (7 người, 2 features)
[[     165  8000000]
 [     168  9000000]
 [     170 10000000]
 [     172 12000000]
 [     175 15000000]
 [     178 18000000]
 [     180 20000000]]

2. Dataset 3 chiều (Height, Income, Age):
   Shape: (7, 3)
   → (7 người, 3 features)
[[     165  8000000       25]
 [     168  9000000       28]
 [     170 10000000       30]
 [     172 12000000       32]
 [     175 15000000       35]
 [     178 18000000       38]
 [     180 20000000       40]]

3. Giả sử có 10 features:
   Shape: (7, 10)
   → (7 người, 10 features)

   Đây vẫn chỉ là MẢNG 2D trong code!
   Nhưng biểu diễn dữ liệu 10-chiều trong không gian toán học!

Code mẫu trong C#

// C# - Tư duy ĐÚNG
public class DataPoint
{
    public double[] Features { get; set; }  // N chiều
    public bool Label { get; set; }
}

// Ví dụ sử dụng
var person1 = new DataPoint 
{ 
    Features = new double[] { 165, 8000000 },  // 2 chiều
    Label = false 
};

var person2 = new DataPoint 
{ 
    Features = new double[] { 165, 8000000, 25, 70, 1.2 },  // 5 chiều
    Label = false 
};

// Hoặc dùng List<List<double>> cho toàn bộ dataset
List<List<double>> dataset = new List<List<double>>
{
    new List<double> { 165, 8000000, 25 },  // Người 1
    new List<double> { 168, 9000000, 28 },  // Người 2
    // ... có thể có bao nhiêu chiều cũng được
};

Code mẫu trong Python

# Cell 13: Tư duy đúng trong Python
# Dùng NumPy array - LUÔN LUÔN là mảng 2D
# Dù dữ liệu của bạn có 2, 3, 10, 100, 1000 features

# Ví dụ 1: 2 features
X_2d = np.array([
    [165, 8000000],
    [168, 9000000],
    [170, 10000000]
])
print(f"2 features: shape = {X_2d.shape}")  # (3, 2)

# Ví dụ 2: 5 features
X_5d = np.array([
    [165, 8000000, 25, 70, 1.75],
    [168, 9000000, 28, 72, 1.78],
    [170, 10000000, 30, 75, 1.80]
])
print(f"5 features: shape = {X_5d.shape}")  # (3, 5)

# Ví dụ 3: 100 features
X_100d = np.random.rand(50, 100)  # 50 người, 100 features
print(f"100 features: shape = {X_100d.shape}")  # (50, 100)

print("\n✅ Trong code, tất cả đều là MẢNG 2D!")
print("✅ Chiều thứ 1: số lượng samples (người)")
print("✅ Chiều thứ 2: số lượng features (thuộc tính)")
Kết quả
2 features: shape = (3, 2)
5 features: shape = (3, 5)
100 features: shape = (50, 100)

✅ Trong code, tất cả đều là MẢNG 2D!
✅ Chiều thứ 1: số lượng samples (người)
✅ Chiều thứ 2: số lượng features (thuộc tính)

Phần 6: Visualize N-chiều

Nếu không thể vẽ được 4D, 5D trở lên thì làm sao visualize?

Kỹ thuật 1: Pair Plot (Biểu đồ cặp)

# Cell 14: Pair plot cho nhiều chiều
import seaborn as sns

# Chỉ dùng 4 features để demo
df_4d = df[['Height', 'Income', 'Age', 'BuyHouse']].copy()

# Tạo pair plot
sns.pairplot(df_4d, hue='BuyHouse', palette={False: 'red', True: 'green'})
plt.suptitle('Pair Plot: Xem tất cả các cặp chiều', y=1.02, fontsize=16, fontweight='bold')
plt.show()

print("Pair plot giúp bạn xem mối quan hệ giữa TẤT CẢ các cặp features.")

Kỹ thuật 2: PCA - Giảm chiều để visualize

# Cell 15: Dùng PCA để giảm từ N-chiều về 2D/3D
from sklearn.decomposition import PCA

# Tạo dataset 10 chiều
X_10d = df[all_features].values
y = df['BuyHouse'].values

# Chuẩn hóa
X_10d_scaled = StandardScaler().fit_transform(X_10d)

# Giảm từ 10 chiều về 2 chiều
pca = PCA(n_components=2)
X_2d_pca = pca.fit_transform(X_10d_scaled)

# Vẽ
plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)
plt.title(f'Dữ liệu gốc: {X_10d_scaled.shape[1]} chiều\n(không thể vẽ được!)', 
          fontsize=13, fontweight='bold')
plt.text(0.5, 0.5, f'Shape: {X_10d_scaled.shape}\n{X_10d_scaled.shape[0]} người\n{X_10d_scaled.shape[1]} features', 
         ha='center', va='center', fontsize=20, bbox=dict(boxstyle='round', facecolor='wheat'))
plt.xlim(0, 1)
plt.ylim(0, 1)
plt.axis('off')

plt.subplot(1, 2, 2)
for i in range(len(X_2d_pca)):
    color = 'red' if not y[i] else 'green'
    plt.scatter(X_2d_pca[i, 0], X_2d_pca[i, 1], c=color, s=150, alpha=0.7)

plt.xlabel(f'PC1 ({pca.explained_variance_ratio_[0]*100:.1f}% variance)', fontsize=11)
plt.ylabel(f'PC2 ({pca.explained_variance_ratio_[1]*100:.1f}% variance)', fontsize=11)
plt.title(f'Sau PCA: 2 chiều\n(có thể visualize!)', fontsize=13, fontweight='bold')
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print(f"PCA giải thích: {sum(pca.explained_variance_ratio_)*100:.2f}% variance của dữ liệu gốc")

Kỹ thuật 3: Dùng màu sắc, kích thước để thể hiện chiều thứ 4, 5

# Cell 16: Dùng màu và size để thể hiện thêm chiều
fig = plt.figure(figsize=(14, 6))

# Vẽ 3D với màu sắc và kích thước
ax = fig.add_subplot(1, 2, 1, projection='3d')

# Giả sử thêm 2 chiều nữa
df['Savings'] = [5, 8, 10, 15, 20, 25, 30]  # Tiết kiệm (triệu)
df['CreditScore'] = [600, 620, 650, 680, 720, 750, 780]  # Điểm tín dụng

# Normalize để làm màu và size
savings_norm = (df['Savings'] - df['Savings'].min()) / (df['Savings'].max() - df['Savings'].min())
credit_norm = (df['CreditScore'] - df['CreditScore'].min()) / (df['CreditScore'].max() - df['CreditScore'].min())

for i, row in df.iterrows():
    color_intensity = savings_norm[i]  # Chiều thứ 4: màu sắc
    size = 50 + credit_norm[i] * 200  # Chiều thứ 5: kích thước
    
    base_color = 'red' if not row['BuyHouse'] else 'green'
    ax.scatter(row['Height'], row['Income'], row['Age'], 
               c=base_color, s=size, alpha=0.3 + color_intensity*0.5)

ax.set_xlabel('Chiều cao', fontsize=10)
ax.set_ylabel('Thu nhập', fontsize=10)
ax.set_zlabel('Tuổi', fontsize=10)
ax.set_title('5 Chiều: 3D + Màu (Savings) + Size (Credit)', fontsize=12, fontweight='bold')

# Vẽ legend
ax2 = fig.add_subplot(1, 2, 2)
ax2.text(0.5, 0.7, 'CHIỀU THỨ 1-3:', ha='center', fontsize=12, fontweight='bold')
ax2.text(0.5, 0.6, 'X, Y, Z = Chiều cao, Thu nhập, Tuổi', ha='center', fontsize=10)
ax2.text(0.5, 0.45, 'CHIỀU THỨ 4:', ha='center', fontsize=12, fontweight='bold')
ax2.text(0.5, 0.35, 'Màu sáng/tối = Tiết kiệm nhiều/ít', ha='center', fontsize=10)
ax2.text(0.5, 0.2, 'CHIỀU THỨ 5:', ha='center', fontsize=12, fontweight='bold')
ax2.text(0.5, 0.1, 'Kích thước lớn/nhỏ = Credit score cao/thấp', ha='center', fontsize=10)
ax2.axis('off')

plt.tight_layout()
plt.show()

print("Bằng cách sử dụng màu sắc và kích thước, ta có thể visualize 5 chiều!")


Tổng kết: Roadmap tư duy về chiều không gian

Chiều Biểu diễn toán học Biểu diễn trong code Cách visualize
1D Một số thực x double x = 5.0; Trục số / Line chart
2D Cặp (x, y) double[] point = {x, y}; Scatter plot 2D
3D Bộ ba (x, y, z) double[] point = {x, y, z}; Scatter plot 3D
4D Bộ bốn (x₁, x₂, x₃, x₄) double[] point = {x1, x2, x3, x4}; 3D + màu sắc
5D Bộ năm (x₁, ..., x₅) double[] point = {x1, ..., x5}; 3D + màu + kích thước
N-D Vector x ∈ ℝⁿ double[] point = new double[n]; PCA / t-SNE / Pair plot

Nguyên tắc vàng

Trong code: Luôn dùng mảng 2D với shape (n_samples, n_features)

Trong toán: Mỗi hàng là 1 vector n-chiều

Khi visualize: Dùng PCA hoặc chọn 2-3 chiều quan trọng nhất

Bài tập thực hành

# Cell 17: Bài tập - Thử với dataset của bạn
print("BÀI TẬP:")
print("1. Tạo dataset của riêng bạn với ít nhất 5 features")
print("2. Chuẩn hóa dữ liệu bằng StandardScaler")
print("3. Vẽ pair plot để xem mối quan hệ")
print("4. Dùng PCA giảm về 2D và visualize")
print()
print("Template code:")
print("""
# Tạo dataset
my_data = pd.DataFrame({
    'Feature1': [...],
    'Feature2': [...],
    # Thêm features khác
    'Label': [...]
})

# Chuẩn hóa
X = my_data.drop('Label', axis=1)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# PCA
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

# Visualize
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=my_data['Label'])
plt.show()
""")

Kết luận

Qua bài viết này, bạn đã học được:

  1. Tại sao cần normalization: Để các features có tỷ lệ đóng góp cân bằng
  2. Cách tư duy về N-chiều: Không phải mảng 3D, 4D... mà là vector
  3. Cách code đúng: Luôn dùng mảng 2D (n_samples, n_features)
  4. Cách visualize N-chiều: PCA, pair plot, màu sắc, kích thước

Điều quan trọng nhất: Đừng bị giới hạn bởi khả năng visualize 3D! Trong Machine Learning, dữ liệu có thể có hàng trăm, hàng nghìn features (chiều), nhưng cách xử lý vẫn giống nhau.

"Dữ liệu 1000 chiều không khó hơn dữ liệu 3 chiều. Nó chỉ là 1 mảng 2D với shape (n, 1000) thôi!"


Bài viết sử dụng Python 3.x, NumPy, Pandas, Matplotlib, Scikit-learn. Code có thể chạy trực tiếp trên Google Colab.

Nhận xét

Bài đăng phổ biến từ blog này

[ASP.NET MVC] Authentication và Authorize

Một trong những vấn đề bảo mật cơ bản nhất là đảm bảo những người dùng hợp lệ truy cập vào hệ thống. ASP.NET đưa ra 2 khái niệm: Authentication và Authorize Authentication xác nhận bạn là ai. Ví dụ: Bạn có thể đăng nhập vào hệ thống bằng username và password hoặc bằng ssh. Authorization xác nhận những gì bạn có thể làm. Ví dụ: Bạn được phép truy cập vào website, đăng thông tin lên diễn đàn nhưng bạn không được phép truy cập vào trang mod và admin.

ASP.NET MVC: Cơ bản về Validation

Validation (chứng thực) là một tính năng quan trọng trong ASP.NET MVC và được phát triển trong một thời gian dài. Validation vắng mặt trong phiên bản đầu tiên của asp.net mvc và thật khó để tích hợp 1 framework validation của một bên thứ 3 vì không có khả năng mở rộng. ASP.NET MVC2 đã hỗ trợ framework validation do Microsoft phát triển, tên là Data Annotations. Và trong phiên bản 3, framework validation đã hỗ trợ tốt hơn việc xác thực phía máy khách, và đây là một xu hướng của việc phát triển ứng dụng web ngày nay.

Tổng hợp một số kiến thức lập trình về Amibroker

Giới thiệu về Amibroker Amibroker theo developer Tomasz Janeczko được xây dựng dựa trên ngôn ngữ C. Vì vậy bộ code Amibroker Formula Language sử dụng có syntax khá tương đồng với C, ví dụ như câu lệnh #include để import hay cách gói các object, hàm trong các block {} và kết thúc câu lệnh bằng dấu “;”. AFL trong Amibroker là ngôn ngữ xử lý mảng (an array processing language). Nó hoạt động dựa trên các mảng (các dòng/vector) số liệu, khá giống với cách hoạt động của spreadsheet trên excel.