0964 456 787 [email protected]

Trong bài viết này tôi sẽ giới thiệu các loss function thường đươc dùng trong các bài toán Machine Learning. Ở đây tôi chỉ giới thiệu với trường hợp binary class:

Cross Entropy

Đây là 1 hàm khá là kinh điểm. Cho \( P(Y = 0) = p\) khi đó \(P(Y = 1) = (1-p)\). Giá trị dự đoán sẽ được cho qua hàm logistic hoặc sigmoid \(\mathbf{P}(\hat{Y} = 0) = \frac{1}{1 + e^{-x}} = \hat{p}\) và \(\mathbf{P}(\hat{Y} = 1) = 1 – \frac{1}{1 + e^{-x}} = 1 – \hat{p}\). Khi đó hàm Cross Entropy được định nghĩa như sau:

\(\text{CE}\left(p, \hat{p}\right) = -\left(p \log\left(\hat{p}\right) + (1-p) \log\left(1 – \hat{p}\right)\right)\)

Trong Keras framework hàm này được định nghĩa trong function binary_crossentropy(y_true, y_pred) trong Tensorflow hàm này được định nghĩa là softmax_cross_entropy_with_logits_v2

Weighted Cross Entropy

Weighted Cross Entropy (WCE) là một dạng phát triển từ CE nó thường được dùng trong các trường hợp các class không cân bằng (imbalanced class). Ví dụ, khi bạn có một bức ảnh với 10% pixel màu đen và 90% các pixel màu trắng, khi đó CE sẽ không hoạt động tốt trong trường hợp này.

WCE được định nghĩa như sau:

\(\text{WCE}\left(p, \hat{p}\right) = -\left(\beta p \log\left(\hat{p}\right) + (1-p) \log\left(1 – \hat{p}\right)\right)\)

Để giảm số lượng các false negatives chúng ta gán \(\beta > 1\). Để giảm số lượng các false positive chúng ta gán \(\beta < 1\)

Trong TensorFlow, loss function này được định nghĩa trong weighted_cross_entropy_with_logits. Đối với Keras chúng ta có thể sử dụng hàm sau:

  1. def weighted_cross_entropy(beta):
  2.   def convert_to_logits(y_pred):
  3.       # see https://github.com/tensorflow/tensorflow/blob/r1.10/tensorflow/python/keras/backend.py#L3525
  4.       y_pred = tf.clip_by_value(y_pred, tf.keras.backend.epsilon(), 1 - tf.keras.backend.epsilon())
  5.  
  6.       return tf.log(y_pred / (1 - y_pred))
  7.  
  8.   def loss(y_true, y_pred):
  9.     y_pred = convert_to_logits(y_pred)
  10.     loss = tf.nn.weighted_cross_entropy_with_logits(logits=y_pred, targets=y_true, pos_weight=beta)
  11.  
  12.     return tf.reduce_mean(loss)
  13.  
  14.   return loss

Ở đây hàm convert_to_logits là cần thiết vì chúng ta đã sử dụng hàm sigmoid đối với y_pred tại lớp cuối cùng trong kiến trúc CNN. Vì vậy để đảo ngược bước này, chúng ta phải tính

\(\log\left(\frac{\hat{y}}{1 – \hat{y}}\right) = \log\left(\frac{\frac{1}{1 + e^{-x}}}{1 – \frac{1}{1 + e^{-x}}}\right) = x\)

Balanced Cross Entropy

Balanced Cross Entropy (BCE) tương tự như WCE. Chỉ có 1 thay đổi nhỏ là trọng số cũng sẽ được gán cho các mẫu negative.

BCE được định nghĩa như sau:

\(\text{BCE}\left(p, \hat{p}\right) = -\left(\beta p \log\left(\hat{p}\right) + (1 – \beta)(1-p) \log\left(1 – \hat{p}\right)\right)\)

Trong Keras, nó có thể được định nghĩa như sau:

  1. def balanced_cross_entropy(beta):
  2.   def convert_to_logits(y_pred):
  3.       # see https://github.com/tensorflow/tensorflow/blob/r1.10/tensorflow/python/keras/backend.py#L3525
  4.       y_pred = tf.clip_by_value(y_pred, tf.keras.backend.epsilon(), 1 - tf.keras.backend.epsilon())
  5.  
  6.       return tf.log(y_pred / (1 - y_pred))
  7.  
  8.   def loss(y_true, y_pred):
  9.     y_pred = convert_to_logits(y_pred)
  10.     pos_weight = beta / (1 - beta)
  11.     loss = tf.nn.weighted_cross_entropy_with_logits(logits=y_pred, targets=y_true, pos_weight=pos_weight)
  12.  
  13.     return tf.reduce_mean(loss * (1 - beta))
  14.  
  15.   return loss

Khi \(\beta = 1\),  có nghĩa giá trị của phần \( (1 – \beta)(1-p) \log\left(1 – \hat{p}\right))\) sẽ bằng 0. Trường hợp này có thể xảy ra, khi chúng ta không gán cố định giá trị \(\beta\). Trong 1 số paper người ta có sử dụng như sau:

  1. <strong>beta = tf.reduce_sum(1 - y_true) / (BATCH_SIZE * HEIGHT * WIDTH)</strong>

Trong trường hợp này, người ta thường thêm vào giá trị của \(\beta\) một giá trị nhỏ \(\epsilon\) tf.keras.backend.epsilon() hoặc sử dụng tf.clip_by_value

Focal Loss

Focal loss (FL) cố gắng giảm sự ảnh hưởng của các mẫu có nhiều trong dữ liệu để tập trung và các mẫu có số lượng ít trong dữ liệu.

FL được định nghĩa như sau:

\(\text{FL}\left(p, \hat{p}\right) = -\left(\alpha (1 – \hat{p})^{\gamma} p \log\left(\hat{p}\right) + (1 – \alpha) \hat{p}^{\gamma} (1-p) \log\left(1 – \hat{p}\right)\right)\)

Khi \(\gamma= 0\), thì nó trở lại với công thức của BCE.

Đối với hàm này chúng ta không thể sử dụng hàm weighted_cross_entropy_with_logits để xây dựng FL trong Keras. Chúng ta sẽ giải quyết vấn đề này bằng các phép biến đổi như sau:
\(\begin{align*}
&= \alpha(1 – \hat{p})^{\gamma} p \log\left(1 + e^{-x}\right) – \left(1 – \alpha\right)\hat{p}^{\gamma}(1-p) \log\left(\frac{e^{-x}}{1 + e^{-x}}\right)\\
&= \alpha(1 – \hat{p})^{\gamma}p \log\left(1 + e^{-x}\right) – \left(1 – \alpha\right)\hat{p}^{\gamma}\left(1-p\right)\left(-x – \log\left(1 + e^{-x}\right)\right)\\
&= \alpha(1 – \hat{p})^{\gamma}p \log\left(1 + e^{-x}\right) + \left(1 – \alpha\right)\hat{p}^{\gamma}\left(1-p\right)\left(x + \log\left(1 + e^{-x}\right)\right)\\
&= \log\left(1 + e^{-x}\right)\left(\alpha (1 – \hat{p})^{\gamma} p + (1-\alpha)\hat{p}^{\gamma}(1-p)\right) + x(1 – \alpha)\hat{p}^{\gamma}(1 – p)\\
&= \log\left(e^{-x}(1 + e^{x})\right)\left(\alpha (1 – \hat{p})^{\gamma} p + (1-\alpha)\hat{p}^{\gamma}(1-p)\right) + x(1 – \alpha)\hat{p}^{\gamma}(1 – p)\\
&= \left(\log\left(1 + e^{x}\right) – x\right)\left(\alpha (1 – \hat{p})^{\gamma} p + (1-\alpha)\hat{p}^{\gamma}(1-p)\right) + x(1 – \alpha)\hat{p}^{\gamma}(1 – p)\\
&= \left(\log\left(1 + e^{-|x|}\right) + \max(-x, 0)\right)\left(\alpha (1 – \hat{p})^{\gamma} p + (1-\alpha)\hat{p}^{\gamma}(1-p)\right) + x(1 – \alpha)\hat{p}^{\gamma}(1 – p)\\
\end{align*}\)

  1. def focal_loss(alpha=0.25, gamma=2):
  2.   def focal_loss_with_logits(logits, targets, alpha, gamma, y_pred):
  3.     weight_a = alpha * (1 - y_pred) ** gamma * targets
  4.     weight_b = (1 - alpha) * y_pred ** gamma * (1 - targets)
  5.  
  6.     return (tf.log1p(tf.exp(-tf.abs(logits))) + tf.nn.relu(-logits)) * (weight_a + weight_b) + logits * weight_b 
  7.  
  8.   def loss(y_true, y_pred):
  9.     y_pred = tf.clip_by_value(y_pred, tf.keras.backend.epsilon(), 1 - tf.keras.backend.epsilon())
  10.     logits = tf.log(y_pred / (1 - y_pred))
  11.  
  12.     loss = focal_loss_with_logits(logits=logits, targets=y_true, alpha=alpha, gamma=gamma, y_pred=y_pred)
  13.  
  14.     return tf.reduce_mean(loss)
  15.  
  16.   return loss

Distance to the nearest cell

Đối với bài toán segmentation image người ta thêm vào hàm BCE một hàm tính khoảng cách để yêu cầu mạng CNN học tại các vị trí biên giữa 2 class.

\(\text{BCE}\left(p, \hat{p}\right) + w_0\cdot\exp\left(-\frac{(d_1(x) + d_2(x))^2}{2\sigma^2}\right)\)

khi \(d_1(x)\) và \(d_2(x)\) là 2 hàm tính khoảng các gần nhất và khoảng cách gần thứ 2 của cell.

Hàm này được thể hiện như sau:

  1. from scipy.spatial import distance_matrix
  2. import numpy as np
  3.  
  4. ...
  5.  
  6. not_zeros = np.argwhere(img != 0)
  7. zeros = np.argwhere(img == 0)
  8.  
  9. dist_matrix = distance_matrix(zeros, not_zeros, p=2)
  10. output = np.zeros((HEIGHT, WIDTH, 1), dtype=np.uint8)
  11.  
  12. i = 0
  13. dist = np.min(dist_matrix, axis=1)
  14. for y in range(HEIGHT):
  15.   for x in range(WIDTH):
  16.     if img[y,x] == 0:
  17.       output[y,x] = dist[i]
  18.       i += 1
  19.  
  20. ...

Ví dụ, phía bên trái là một mask và phía bên phải là 1 map trọng số tương ứng:

Pixel càng đen, trọng số càng cao. Hàm BCE chỉ thay đổi 1 dòng pos_weight = beta / (1 – beta) + tf.exp(-weights). Và bỏ qua phần ma trận trong số ở input:

  1. from functools import partial
  2.  
  3. def loss_function(y_true, y_pred, weights):
  4. ...
  5.  
  6. weight_input = Input(shape=(HEIGHT, WIDTH))
  7. loss = partial(loss_function, weights=weight_input)
  8.  
  9. Đọc thêm về: <a href="http://naebolo.com/gioi-thieu-ve-accuracy-precision-recall-va-f1/">Giới thiệu về Accuracy, Precision, Recall và F1</a>