Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Lưu ý: Mình khuyên các bạn nên vào thẳng notebook nha vì mình chuyển file ipynb sang html nên đôi khi có lỗi
Link Notebook : https://colab.research.google.com/drive/1I8wQUdcUU9XwWYRjiwTtg11RzuTzwMRh?usp=sharing
Trong bước đầu tiên, bạn sẽ nhận được các đặc trưng (features) và nhãn (labels). Mục tiêu của bạn là tối thiểu hóa hàm chi phí (cost function) bằng cách điều chỉnh các tham số (parameters hoặc weights). Quá trình này sử dụng các thuật toán tối ưu hóa như Gradient Descent để cập nhật trọng số (weights) và bias. Qua đó, bạn sẽ cải thiện độ chính xác của giá trị đầu ra (predicted values), sao cho chúng gần gũi nhất với giá trị thực tế (ground truth). Điều này giúp mô hình học được mối quan hệ giữa input và output.
Trong tuần đầu thì sài logistic regression để phân tích cảm xúc của một câu với các câu là các features
Note :
1 : positive sentiment
0 : negative sentiment
Ứng với mỗi tweet hoặc đoạn văn bản, bạn có thể biểu diễn nó dưới dạng các vector với kích thước V, trong đó V là kích thước của vocabulary (tập từ vựng). Mỗi từ trong từ vựng được gán một chỉ số (index). Nếu từ xuất hiện trong tweet, giá trị tại vị trí tương ứng sẽ là 1, nếu không thì sẽ là 0.
Ví dụ: Nếu bạn có tweet “I am happy because I am learning NLP”, và tập từ vựng là [“I”, “am”, “happy”, “because”, “learning”, “NLP”, “machine”], thì vector biểu diễn sẽ là:
# Vocabulary
vocab = ["I", "am", "happy", "because", "learning", "NLP", "machine"]
# Tweet
tweet = "I am happy because I am learning NLP"
# Biểu diễn vector
vector = [1 if word in tweet.split() else 0 for word in vocab]
print("Vector biểu diễn:", vector)
Như bạn có thể thấy, khi V càng lớn, vector trở nên thưa thớt hơn(sparse). Hơn nữa, chúng ta sẽ có nhiều đặc trưng hơn và sẽ phải huấn luyện θ với V tham số. Điều này có thể dẫn đến thời gian huấn luyện lâu hơn và thời gian dự đoán lớn hơn.
Cho một tập dữ liệu (corpus) gồm các tweet tích cực (positive) và tiêu cực (negative), nhiệm vụ của bạn là mã hóa mỗi tweet thành một vector đặc trưng. Trước đây, vector này có kích thước V (kích thước của vocabulary). Bây giờ, bạn sẽ mã hóa nó thành một vector kích thước 3, bao gồm:
Bias: Một giá trị cố định, thường là 1.
Positive Feature: Tần suất của các từ trong tweet xuất hiện trong lớp tích cực.
Negative Feature: Tần suất của các từ trong tweet xuất hiện trong lớp tiêu cực.
Để thực hiện điều này, bạn cần tạo một dictionary freqs, ánh xạ mỗi từ và lớp của nó (tích cực hoặc tiêu cực) với số lần từ đó xuất hiện trong lớp tương ứng.
# Tập dữ liệu (corpus)
corpus = [
("I am happy", "positive"),
("I am sad", "negative"),
("I am learning NLP", "positive"),
("I am not happy", "negative"),
]
# Tạo từ điển tần suất
from collections import defaultdict
freqs = defaultdict(lambda: {"positive": 0, "negative": 0})
for tweet, label in corpus:
for word in tweet.split():
freqs[word][label] += 1
print("Từ điển tần suất:", freqs)
Từ điển tần suất: defaultdict(<function <lambda> at 0x7d6b41b1ac20>, {'I': {'positive': 2, 'negative': 2}, 'am': {'positive': 2, 'negative': 2}, 'happy': {'positive': 1, 'negative': 1}, 'sad': {'positive': 0, 'negative': 1}, 'learning': {'positive': 1, 'negative': 0}, 'NLP': {'positive': 1, 'negative': 0}, 'not': {'positive': 0, 'negative': 1}})
Giả sử bạn có tweet “I am sad, I am not learning NLP” và muốn biểu diễn nó thành vector đặc trưng [bias, positive_feature, negative_feature].
# Tính vector đặc trưng
def extract_features(tweet, freqs):
words = tweet.split()
positive_feature = sum(freqs[word]["positive"] for word in words if word in freqs)
negative_feature = sum(freqs[word]["negative"] for word in words if word in freqs)
bias = 1
return [bias, positive_feature, negative_feature]
# Tweet mới
tweet = "I am sad I am not learning NLP"
features = extract_features(tweet, freqs)
print("Vector đặc trưng:", features)
Vector đặc trưng: [1, 10, 10]
Dưới đây là cách bạn có thể thực hiện bước tiền xử lý (preprocessing) trong xử lý văn bản, với các ví dụ chi tiết bằng tiếng Anh:
Khi tiền xử lý, bạn cần thực hiện các bước sau:
Giả sử bạn có tweet sau:
@YMourri and @AndrewYNg are tuning a GREAT AI model at https://deeplearning.ai!!!
Sau khi thực hiện tiền xử lý, kết quả sẽ như sau:
Loại bỏ handle và URL:
@YMourri
, @AndrewYNg
, và https://deeplearning.ai
.Chuyển chuỗi thành từ:
["and", "are", "tuning", "a", "GREAT", "AI", "model", "at"]
.Loại bỏ stop words:
["tuning", "GREAT", "AI", "model"]
.Chuyển về gốc từ (stemming):
Chuyển thành chữ thường:
["tun", "great", "ai", "model"]
.import nltk
nltk.download('punkt')
nltk.download('stopwords')
[nltk_data] Downloading package punkt to /root/nltk_data... [nltk_data] Package punkt is already up-to-date! [nltk_data] Downloading package stopwords to /root/nltk_data... [nltk_data] Package stopwords is already up-to-date!
True
import re
from nltk.stem import PorterStemmer
# Hàm tiền xử lý
def preprocess(text):
# Loại bỏ handle và URL
text = re.sub(r'@[\w]+', '', text) # Loại bỏ handle
text = re.sub(r'http\S+', '', text) # Loại bỏ URL
# Tách chuỗi thành từ
tokens = re.findall(r'\b\w+\b', text)
# Loại bỏ stop words (danh sách cơ bản)
stop_words = set(['and', 'is', 'a', 'on', 'the', 'are', 'to', 'in', 'of', 'at', 'for', 'with'])
tokens = [word for word in tokens if word.lower() not in stop_words]
# Stemming
ps = PorterStemmer()
tokens = [ps.stem(word) for word in tokens]
# Chuyển tất cả thành chữ thường
tokens = [word.lower() for word in tokens]
return tokens
# Ví dụ tweet
tweet = "@YMourri and @AndrewYNg are tuning a GREAT AI model at https://deeplearning.ai!!!"
processed_text = preprocess(tweet)
print(processed_text)
['tune', 'great', 'ai', 'model']
Bắt đầu với văn bản đầu vào, bạn thực hiện tiền xử lý (preprocessing), sau đó trích xuất các đặc trưng (feature extraction) để chuyển đổi văn bản thành dạng biểu diễn số như sau:
Ma trận X sẽ có kích thước là (m, 3)
với m
là số lượng ví dụ, và 3 là số lượng đặc trưng được trích xuất từ mỗi ví dụ.
Khi triển khai mã, quá trình có thể được viết như sau:
(m, 3)
.import numpy as np
from nltk.stem import PorterStemmer
import re
def create_frequency_dictionary(tweets, labels):
"""
Tạo từ điển tần suất từ tweets và nhãn tương ứng.
Tham số:
tweets (list): Danh sách các tweet đã qua tiền xử lý
labels (list): Danh sách nhãn (0: tiêu cực, 1: tích cực)
Trả về:
dict: Từ điển chứa tần suất của từng từ trong tweet tích cực và tiêu cực
"""
freqs = {}
# Duyệt qua từng cặp tweet và nhãn
for tweet, label in zip(tweets, labels):
for word in tweet:
# Khởi tạo từ mới trong từ điển nếu chưa tồn tại
if word not in freqs:
freqs[word] = {"positive": 0, "negative": 0}
# Cập nhật tần suất dựa vào nhãn
if label == 1:
freqs[word]["positive"] += 1
else:
freqs[word]["negative"] += 1
return freqs
def preprocess_tweet(tweet):
"""
Tiền xử lý tweet bằng cách loại bỏ handles, URLs,
tách từ, loại bỏ stop words và áp dụng stemming.
Tham số:
tweet (str): Nội dung tweet gốc
Trả về:
list: Danh sách các token đã qua xử lý
"""
# Loại bỏ handle và URL
tweet = re.sub(r'@[\w]+', '', tweet) # Xóa các handle Twitter
tweet = re.sub(r'http\S+', '', tweet) # Xóa các URL
# Tách từ và chuyển thành chữ thường
tokens = re.findall(r'\b\w+\b', tweet.lower())
# Loại bỏ stop words (danh sách cơ bản)
stop_words = set(['and', 'is', 'a', 'on', 'the', 'are', 'to', 'in', 'of', 'at', 'for', 'with'])
tokens = [word for word in tokens if word not in stop_words]
# Áp dụng stemming
ps = PorterStemmer()
tokens = [ps.stem(word) for word in tokens]
return tokens
def extract_features(tweet, freqs):
"""
Trích xuất đặc trưng từ tweet đã qua xử lý sử dụng từ điển tần suất.
Tham số:
tweet (list): Danh sách các token đã qua xử lý
freqs (dict): Từ điển tần suất
Trả về:
list: Vector đặc trưng [bias, tổng_tích_cực, tổng_tiêu_cực]
"""
# Thêm bias term
bias = 1
# Tính tổng tần suất tích cực và tiêu cực
positive_sum = sum(freqs.get(word, {"positive": 0})["positive"] for word in tweet)
negative_sum = sum(freqs.get(word, {"negative": 0})["negative"] for word in tweet)
return [bias, positive_sum, negative_sum]
# Ví dụ sử dụng
if __name__ == "__main__":
# Dữ liệu mẫu
raw_tweets = [
"This movie is great! I loved it",
"Terrible waste of time, awful movie",
"Amazing performance by the actors!",
"I really disliked this show, boring"
]
labels = [1, 0, 1, 0] # 1: tích cực, 0: tiêu cực
# Bước 1: Tiền xử lý tất cả tweets
processed_tweets = [preprocess_tweet(tweet) for tweet in raw_tweets]
# Bước 2: Tạo từ điển tần suất
freqs = create_frequency_dictionary(processed_tweets, labels)
# Bước 3: Tạo ma trận đặc trưng X
m = len(processed_tweets)
X = np.zeros((m, 3)) # Ma trận có m hàng và 3 cột (bias, positive, negative)
# Bước 4: Trích xuất đặc trưng cho từng tweet
for i, tweet in enumerate(processed_tweets):
X[i] = extract_features(tweet, freqs)
print("Kích thước ma trận đặc trưng:", X.shape)
print("\nMa trận đặc trưng:")
print(X)
# Ví dụ xử lý tweet mới
new_tweet = "@user Just watched a new movie! It was fantastic! #movies"
processed_new_tweet = preprocess_tweet(new_tweet)
new_features = extract_features(processed_new_tweet, freqs)
print("\nĐặc trưng của tweet mới:", new_features)
Kích thước ma trận đặc trưng: (4, 3) Ma trận đặc trưng: [[1. 6. 3.] [1. 1. 5.] [1. 4. 0.] [1. 2. 6.]] Đặc trưng của tweet mới: [1, 3, 2]
Sau khi chạy mã trên, bạn sẽ thấy ma trận X với kích thước (4, 3)
(vì có 4 tweet và 3 đặc trưng), chứa các giá trị đặc trưng của mỗi tweet.
Mã trên thực hiện quy trình: tiền xử lý dữ liệu (như loại bỏ handle và URL), xây dựng từ điển tần suất, trích xuất các đặc trưng từ các tweet và lưu trữ chúng vào ma trận X để sử dụng cho các bước phân tích tiếp theo.
Hồi quy logistic sử dụng hàm sigmoid, một hàm cho ra xác suất trong khoảng từ 0 đến 1.
Hàm sigmoid với tham số trọng số $\theta$ và đầu vào $x^{(i)}$ được định nghĩa:
$h_\theta(x^{(i)}) = \frac{1}{1 + e^{-\theta^T x^{(i)}}}$
Lưu ý rằng:
Với một tweet, ta có thể biến đổi nó thành vector và đưa qua hàm sigmoid để dự đoán:
$\hat{y}^{(i)} = h_\theta(x^{(i)}) = \frac{1}{1 + e^{-\theta^T x^{(i)}}}$
import numpy as np
# Định nghĩa hàm sigmoid
def sigmoid(z):
return 1 / (1 + np.exp(-z))
# Ví dụ về tham số theta và đầu vào x
theta = np.array([0.5, -0.6, 0.2]) # Các tham số trọng số
x_i = np.array([1, 2, 3]) # Đầu vào vector (giả sử x_i là một vector với 3 đặc trưng)
# Tính toán dự đoán
z = np.dot(theta, x_i) # Tính giá trị z = theta^T * x_i
prediction = sigmoid(z) # Tính xác suất với hàm sigmoid
print("Dự đoán xác suất: ", prediction)
Dự đoán xác suất: 0.47502081252106
$\textbf{Các bước chính:}$
Khởi tạo: $\theta \sim \mathcal{N}(0, 1)$ (ngẫu nhiên từ phân phối chuẩn)
Forward Pass:
Backward Pass:
Cập nhật trọng số:
Lặp lại bước 2-4 cho đến khi hội tụ.
Trong quá trình trainning tới hội tụ loss sẽ có biểu đồ như sau :
Để đánh giá hiệu quả của mô hình hồi quy logistic, ta cần trải qua quy trình kiểm tra chặt chẽ sau:
Mô hình sẽ đưa ra dự đoán dựa trên đầu ra của hàm sigmoid:
Với tập dữ liệu $X$, ta thực hiện chia thành 3 phần:
Tỷ lệ phân chia phổ biến: 80-10-10
Lưu ý: Tỷ lệ này có thể điều chỉnh tùy thuộc vào kích thước của tập dữ liệu
Công thức tính độ chính xác:
$$ \text{Accuracy} = \frac{\text{Số dự đoán đúng}}{\text{Tổng số mẫu}} = \frac{1}{m} \sum_{i=1}^{m} \mathbb{1}\{\hat{y}^{(i)} = y^{(i)}\} $$
Trong đó:
Mẹo: Ngoài Accuracy, bạn cũng nên xem xét các metrics khác như Precision, Recall và F1-score để đánh giá toàn diện hiệu suất của mô hình.
import numpy as np
# Example dataset
y_true = np.array([1, 0, 1, 1, 0, 1, 0, 0, 1, 0]) # Ground truth labels
y_pred_prob = np.array([0.9, 0.3, 0.8, 0.7, 0.2, 0.9, 0.1, 0.4, 0.85, 0.05]) # Predicted probabilities
# Convert probabilities to binary predictions
y_pred = (y_pred_prob >= 0.5).astype(int)
# Calculate accuracy
accuracy = np.mean(y_pred == y_true)
print(f"Accuracy: {accuracy * 100:.2f}%")
Accuracy: 100.00%
Đầu tiên, ta xem xét xác suất có điều kiện cho một mẫu dữ liệu:
$$P(y|x^{(i)},\theta) = h(x^{(i)},\theta)^{y^{(i)}}(1-h(x^{(i)},\theta))^{(1-y^{(i)})}$$
Giải thích:
Với toàn bộ tập dữ liệu, ta muốn tìm $\theta$ để maximize xác suất nhận được tất cả các mẫu:
$$L(\theta) = \prod_{i=1}^m h(x^{(i)},\theta)^{y^{(i)}}(1-h(x^{(i)},\theta))^{(1-y^{(i)})}$$
Lý do dùng tích $\prod$:
Ta lấy logarit của hàm likelihood vì:
# Minh họa bằng code
x = np.array([0.1, 0.2, 0.3])
print(f"Tích thông thường: {np.prod(x)}") # Có thể rất nhỏ
print(f"Tổng log: {np.sum(np.log(x))}") # Dễ tính toán hơn
Tích thông thường: 0.006000000000000001 Tổng log: -5.115995809754081
$$\log L(\theta) = \sum_{i=1}^m \log P(y^{(i)}|x^{(i)};\theta)$$
$$= \sum_{i=1}^m [y^{(i)}\log h_\theta(x^{(i)}) + (1-y^{(i)})\log(1-h_\theta(x^{(i)}))]$$
Để có hàm chi phí chuẩn, tìm $\theta$ tối ưu, ta cần:
$$\theta_{MLE} = \arg\max_\theta \log L(\theta)$$
Tương đương với việc tối thiểu hóa negative log-likelihood:
$$\theta_{MLE} = \arg\min_\theta [-\log L(\theta)]$$
Chia cho m để có giá trị trung bình:
$$J(\theta) = -\frac{1}{m}\log L(\theta) = -\frac{1}{m}\sum_{i=1}^m [y^{(i)}\log h_\theta(x^{(i)}) + (1-y^{(i)})\log(1-h_\theta(x^{(i)}))]$$
import numpy as np
import matplotlib.pyplot as plt
h = np.linspace(0.01, 0.99, 100)
cost_y1 = -np.log(h)
plt.plot(h, cost_y1)
plt.title('Chi phí khi y=1')
plt.xlabel('h(x)')
plt.ylabel('Chi phí')
plt.grid(True)
plt.show()
cost_y0 = -np.log(1-h)
plt.plot(h, cost_y0)
plt.title('Chi phí khi y=0')
plt.xlabel('h(x)')
plt.ylabel('Chi phí')
plt.grid(True)
plt.show()
Đạo hàm theo $\theta_j$:
$$\frac{\partial}{\partial \theta_j} J(\theta) = -\frac{1}{m}\sum_{i=1}^m (y^{(i)} – h_\theta(x^{(i)}))x_j^{(i)}$$
Tại sao không dùng MSE?
mse_y1 = (1-h)**2
mse_y0 = h**2
plt.figure(figsize=(12, 4))
plt.subplot(121)
plt.plot(h, cost_y1, label='Log Loss')
plt.plot(h, mse_y1, label='MSE')
plt.title('So sánh Loss Functions (y=1)')
plt.legend()
plt.subplot(122)
plt.plot(h, cost_y0, label='Log Loss')
plt.plot(h, mse_y0, label='MSE')
plt.title('So sánh Loss Functions (y=0)')
plt.legend()
plt.show()
💡 Kết luận: Hàm chi phí được thiết kế dựa trên nguyên lý Maximum Likelihood, không chỉ có ý nghĩa toán học mà còn mang lại nhiều ưu điểm thực tiễn trong quá trình tối ưu hóa.
Hàm chi phí có thể được viết dưới dạng vectorized như sau:
$$h = g(X\theta)$$ $$J(\theta) = \frac{1}{m} (-y^T \log(h) – (1-y)^T \log(1-h))$$
Trong đó:
import numpy as np
class LogisticRegressionVectorized:
def __init__(self):
self.theta = None
def sigmoid(self, z):
"""Vectorized sigmoid function"""
return 1 / (1 + np.exp(-z))
def compute_cost(self, X, y, theta):
"""Vectorized cost function"""
m = len(y)
# Tính h = g(X.θ)
h = self.sigmoid(X.dot(theta))
# Tính J(θ) = 1/m * (-y^T.log(h) - (1-y)^T.log(1-h))
J = (1/m) * (-y.T.dot(np.log(h)) - (1-y).T.dot(np.log(1-h)))
return J
# Ví dụ minh họa
np.random.seed(42)
m, n = 100, 3 # 100 mẫu, 3 features
# Tạo dữ liệu mẫu
X = np.random.randn(m, n) # Ma trận features
theta = np.random.randn(n) # Vector tham số
y = np.random.randint(0, 2, m) # Vector nhãn (0/1)
# Tính cost
model = LogisticRegressionVectorized()
cost = model.compute_cost(X, y, theta)
print(f"Chi phí: {cost}")
Chi phí: 0.8519182245306806
!jupyter nbconvert --to html Week_1_NLP_specialization_Logistic_regression\ \(1\).ipynb
[NbConvertApp] Converting notebook Week_1_NLP_specialization_Logistic_regression (1).ipynb to html [NbConvertApp] WARNING | Alternative text is missing on 3 image(s). [NbConvertApp] Writing 1629333 bytes to Week_1_NLP_specialization_Logistic_regression (1).html