Xem trên TensorFlow.org | Chạy trong Google Colab | Xem trên GitHub | Tải xuống sổ ghi chép | Xem mô hình TF Hub |
Hướng dẫn này trình bày cách triển khai Tích hợp Gradients (IG) , một kỹ thuật AI có thể giải thích được giới thiệu trong bài báo về Phân bổ tiên đề cho Mạng sâu . IG nhằm mục đích giải thích mối quan hệ giữa các dự đoán của một mô hình về các tính năng của nó. Nó có nhiều trường hợp sử dụng bao gồm hiểu các nhập tính năng, xác định độ lệch dữ liệu và hiệu suất mô hình gỡ lỗi.
IG đã trở thành một kỹ thuật diễn giải phổ biến do khả năng ứng dụng rộng rãi của nó cho bất kỳ mô hình có thể phân biệt nào (ví dụ: hình ảnh, văn bản, dữ liệu có cấu trúc), dễ thực hiện, giải thích lý thuyết và hiệu quả tính toán so với các phương pháp tiếp cận thay thế cho phép nó mở rộng quy mô đến các mạng lớn và tính năng không gian chẳng hạn như hình ảnh.
Trong hướng dẫn này, bạn sẽ hướng dẫn từng bước về việc triển khai IG để hiểu các nhập tính năng pixel của bộ phân loại hình ảnh. Ví dụ, hãy xem hình ảnh này của một chiếc tàu cứu hỏa đang phun những tia nước. Bạn sẽ phân loại hình ảnh này như một chiếc thuyền cứu hỏa và có thể làm nổi bật các pixel tạo nên chiếc thuyền và vòi rồng là quan trọng đối với quyết định của bạn. Mô hình của bạn cũng sẽ phân loại hình ảnh này thành một chiếc thuyền cứu hỏa sau này trong hướng dẫn này; tuy nhiên, liệu nó có làm nổi bật các pixel tương tự có quan trọng khi giải thích quyết định của nó không?
Trong các hình ảnh dưới đây có tiêu đề "IG Attribution Mask" và "Original + IG Mask Over", bạn có thể thấy rằng thay vào đó, mô hình của bạn làm nổi bật (bằng màu tím), các pixel bao gồm vòi rồng và tia nước của thuyền quan trọng hơn bản thân thuyền. quyết định của nó. Mô hình của bạn sẽ khái quát hóa như thế nào đối với thuyền cứu hỏa mới? Còn thuyền cứu hỏa không có tia nước thì sao? Đọc tiếp để tìm hiểu thêm về cách thức hoạt động của IG và cách áp dụng IG vào các mô hình của bạn để hiểu rõ hơn về mối quan hệ giữa các dự đoán và các tính năng cơ bản của chúng.
Thành lập
import matplotlib.pylab as plt
import numpy as np
import tensorflow as tf
import tensorflow_hub as hub
Tải xuống bộ phân loại hình ảnh được đào tạo trước từ TF-Hub
IG có thể được áp dụng cho bất kỳ mô hình khác biệt nào. Theo tinh thần của bài báo gốc, bạn sẽ sử dụng phiên bản được đào tạo trước của cùng một mô hình, Inception V1, mà bạn sẽ tải xuống từ TensorFlow Hub .
model = tf.keras.Sequential([
hub.KerasLayer(
name='inception_v1',
handle='https://tfhub.dev/google/imagenet/inception_v1/classification/4',
trainable=False),
])
model.build([None, 224, 224, 3])
model.summary()
Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= inception_v1 (KerasLayer) (None, 1001) 6633209 ================================================================= Total params: 6,633,209 Trainable params: 0 Non-trainable params: 6,633,209 _________________________________________________________________
Từ trang mô-đun, bạn cần ghi nhớ những điều sau về Inception V1:
Đầu vào : Hình dạng đầu vào mong đợi cho mô hình là (None, 224, 224, 3)
. Đây là một tensor 4D dày đặc của dtype float32 và shape (batch_size, height, width, RGB channels)
có các phần tử là giá trị màu RGB của pixel được chuẩn hóa thành phạm vi [0, 1]. Phần tử đầu tiên là None
để chỉ ra rằng mô hình có thể có bất kỳ kích thước lô số nguyên nào.
Kết quả đầu ra : Một tf.Tensor
của logits có dạng (batch_size, 1001)
. Mỗi hàng đại diện cho điểm dự đoán của mô hình cho mỗi 1.001 lớp từ ImageNet. Đối với chỉ số lớp được dự đoán hàng đầu của mô hình, bạn có thể sử dụng tf.argmax(predictions, axis=-1)
. Hơn nữa, bạn cũng có thể chuyển đổi đầu ra logit của mô hình thành xác suất dự đoán trên tất cả các lớp bằng cách sử dụng tf.nn.softmax(predictions, axis=-1)
để định lượng độ không chắc chắn của mô hình cũng như khám phá các lớp dự đoán tương tự để gỡ lỗi.
def load_imagenet_labels(file_path):
labels_file = tf.keras.utils.get_file('ImageNetLabels.txt', file_path)
with open(labels_file) as reader:
f = reader.read()
labels = f.splitlines()
return np.array(labels)
imagenet_labels = load_imagenet_labels('https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')
Tải và xử lý trước hình ảnh với tf.image
Bạn sẽ minh họa IG bằng hai hình ảnh từ Wikimedia Commons : Tàu cứu hỏa và Gấu trúc khổng lồ .
def read_image(file_name):
image = tf.io.read_file(file_name)
image = tf.io.decode_jpeg(image, channels=3)
image = tf.image.convert_image_dtype(image, tf.float32)
image = tf.image.resize_with_pad(image, target_height=224, target_width=224)
return image
img_url = {
'Fireboat': 'http://storage.googleapis.com/download.tensorflow.org/example_images/San_Francisco_fireboat_showing_off.jpg',
'Giant Panda': 'http://storage.googleapis.com/download.tensorflow.org/example_images/Giant_Panda_2.jpeg',
}
img_paths = {name: tf.keras.utils.get_file(name, url) for (name, url) in img_url.items()}
img_name_tensors = {name: read_image(img_path) for (name, img_path) in img_paths.items()}
Downloading data from http://storage.googleapis.com/download.tensorflow.org/example_images/San_Francisco_fireboat_showing_off.jpg 3956736/3954129 [==============================] - 0s 0us/step 3964928/3954129 [==============================] - 0s 0us/step Downloading data from http://storage.googleapis.com/download.tensorflow.org/example_images/Giant_Panda_2.jpeg 811008/802859 [==============================] - 0s 0us/step 819200/802859 [==============================] - 0s 0us/step
plt.figure(figsize=(8, 8))
for n, (name, img_tensors) in enumerate(img_name_tensors.items()):
ax = plt.subplot(1, 2, n+1)
ax.imshow(img_tensors)
ax.set_title(name)
ax.axis('off')
plt.tight_layout()
Phân loại hình ảnh
Hãy bắt đầu bằng cách phân loại những hình ảnh này và hiển thị 3 dự đoán hàng đầu tự tin nhất. Sau đây là một hàm tiện ích để lấy k nhãn và xác suất được dự đoán hàng đầu.
def top_k_predictions(img, k=3):
image_batch = tf.expand_dims(img, 0)
predictions = model(image_batch)
probs = tf.nn.softmax(predictions, axis=-1)
top_probs, top_idxs = tf.math.top_k(input=probs, k=k)
top_labels = imagenet_labels[tuple(top_idxs)]
return top_labels, top_probs[0]
for (name, img_tensor) in img_name_tensors.items():
plt.imshow(img_tensor)
plt.title(name, fontweight='bold')
plt.axis('off')
plt.show()
pred_label, pred_prob = top_k_predictions(img_tensor)
for label, prob in zip(pred_label, pred_prob):
print(f'{label}: {prob:0.1%}')
fireboat: 32.6% pier: 12.7% suspension bridge: 5.7%
giant panda: 89.4% teddy: 0.3% gibbon: 0.3%
Tính toán Gradients tích hợp
Mô hình của bạn, Inception V1, là một hàm đã học mô tả ánh xạ giữa không gian đặc điểm đầu vào, giá trị pixel hình ảnh và không gian đầu ra được xác định bởi giá trị xác suất lớp ImageNet từ 0 đến 1. Phương pháp giải thích sớm cho mạng thần kinh được chỉ định điểm quan trọng của tính năng bằng cách sử dụng gradient, cho bạn biết pixel nào có độ dốc cục bộ cao nhất so với dự đoán của mô hình của bạn tại một điểm nhất định dọc theo chức năng dự đoán của mô hình của bạn. Tuy nhiên, độ dốc chỉ mô tả những thay đổi cục bộ trong chức năng dự đoán của mô hình liên quan đến giá trị pixel và không mô tả đầy đủ toàn bộ chức năng dự đoán mô hình của bạn. Khi mô hình của bạn hoàn toàn "học" được mối quan hệ giữa phạm vi của một pixel riêng lẻ và lớp ImageNet chính xác, gradient cho pixel này sẽ bão hòa , nghĩa là ngày càng trở nên nhỏ và thậm chí bằng không. Hãy xem xét chức năng mô hình đơn giản dưới đây:
def f(x):
"""A simplified model function."""
return tf.where(x < 0.8, x, 0.8)
def interpolated_path(x):
"""A straight line path."""
return tf.zeros_like(x)
x = tf.linspace(start=0.0, stop=1.0, num=6)
y = f(x)
fig = plt.figure(figsize=(12, 5))
ax0 = fig.add_subplot(121)
ax0.plot(x, f(x), marker='o')
ax0.set_title('Gradients saturate over F(x)', fontweight='bold')
ax0.text(0.2, 0.5, 'Gradients > 0 = \n x is important')
ax0.text(0.7, 0.85, 'Gradients = 0 \n x not important')
ax0.set_yticks(tf.range(0, 1.5, 0.5))
ax0.set_xticks(tf.range(0, 1.5, 0.5))
ax0.set_ylabel('F(x) - model true class predicted probability')
ax0.set_xlabel('x - (pixel value)')
ax1 = fig.add_subplot(122)
ax1.plot(x, f(x), marker='o')
ax1.plot(x, interpolated_path(x), marker='>')
ax1.set_title('IG intuition', fontweight='bold')
ax1.text(0.25, 0.1, 'Accumulate gradients along path')
ax1.set_ylabel('F(x) - model true class predicted probability')
ax1.set_xlabel('x - (pixel value)')
ax1.set_yticks(tf.range(0, 1.5, 0.5))
ax1.set_xticks(tf.range(0, 1.5, 0.5))
ax1.annotate('Baseline', xy=(0.0, 0.0), xytext=(0.0, 0.2),
arrowprops=dict(facecolor='black', shrink=0.1))
ax1.annotate('Input', xy=(1.0, 0.0), xytext=(0.95, 0.2),
arrowprops=dict(facecolor='black', shrink=0.1))
plt.show();
left : Gradient của mô hình của bạn cho pixel
x
dương từ 0,0 đến 0,8 nhưng chuyển đến 0,0 trong khoảng 0,8 đến 1,0. Pixelx
rõ ràng có tác động đáng kể đến việc đẩy mô hình của bạn đến 80% xác suất dự đoán trên loại thực. Nó có ý nghĩa rằng tầm quan trọng của pixelx
là nhỏ hoặc không liên tục?đúng : Trực giác đằng sau IG là tích lũy các gradient cục bộ của pixel
x
và quy tầm quan trọng của nó dưới dạng điểm số cho số lượng nó cộng hoặc trừ vào xác suất lớp đầu ra tổng thể của mô hình của bạn. Bạn có thể chia nhỏ và tính IG thành 3 phần:- nội suy các bước nhỏ dọc theo một đường thẳng trong không gian đối tượng giữa 0 (đường cơ sở hoặc điểm bắt đầu) và 1 (giá trị của pixel đầu vào)
- tính toán độ dốc ở mỗi bước giữa các dự đoán của mô hình của bạn đối với mỗi bước
- ước tính tích phân giữa đường cơ sở và đầu vào của bạn bằng cách tích lũy (trung bình tích lũy) các gradient cục bộ này.
Để củng cố trực giác này, bạn sẽ đi qua 3 phần này bằng cách áp dụng IG vào hình ảnh ví dụ "Fireboat" bên dưới.
Thiết lập một đường cơ sở
Đường cơ sở là hình ảnh đầu vào được sử dụng làm điểm bắt đầu để tính mức độ quan trọng của đối tượng địa lý. Theo trực giác, bạn có thể nghĩ về vai trò giải thích của đường cơ sở là thể hiện tác động của việc không có từng pixel đối với dự đoán "Thuyền cứu hỏa" để tương phản với tác động của từng pixel đối với dự đoán "Thuyền cứu hỏa" khi xuất hiện trong hình ảnh đầu vào. Do đó, việc lựa chọn đường cơ sở đóng một vai trò trung tâm trong việc giải thích và hình dung các điểm nhập khẩu của tính năng pixel. Để thảo luận thêm về lựa chọn đường cơ sở, hãy xem các tài nguyên trong phần "Các bước tiếp theo" ở cuối hướng dẫn này. Ở đây, bạn sẽ sử dụng một hình ảnh màu đen có các giá trị pixel đều bằng không.
Các lựa chọn khác mà bạn có thể thử nghiệm bao gồm hình ảnh toàn màu trắng hoặc hình ảnh ngẫu nhiên mà bạn có thể tạo bằng tf.random.uniform(shape=(224,224,3), minval=0.0, maxval=1.0)
.
baseline = tf.zeros(shape=(224,224,3))
plt.imshow(baseline)
plt.title("Baseline")
plt.axis('off')
plt.show()
Giải nén các công thức thành mã
Công thức cho Gradients Tích hợp như sau:
\(IntegratedGradients_{i}(x) ::= (x_{i} - x'_{i})\times\int_{\alpha=0}^1\frac{\partial F(x'+\alpha \times (x - x'))}{\partial x_i}{d\alpha}\)
ở đâu:
\(_{i}\) = tính năng
\(x\) = đầu vào
\(x'\) = baseline
\(\alpha\) = hằng số nội suy để xáo trộn các tính năng bằng
Trong thực tế, việc tính toán một tích phân xác định không phải lúc nào cũng khả thi về mặt số học và có thể tốn kém về mặt tính toán, vì vậy bạn tính toán xấp xỉ số sau:
\(IntegratedGrads^{approx}_{i}(x)::=(x_{i}-x'_{i})\times\sum_{k=1}^{m}\frac{\partial F(x' + \frac{k}{m}\times(x - x'))}{\partial x_{i} } \times \frac{1}{m}\)
ở đâu:
\(_{i}\) = feature (pixel riêng lẻ)
\(x\) = đầu vào (bộ căng hình ảnh)
\(x'\) = baseline (ảnh căng)
\(k\) = hằng số nhiễu loạn tính năng được chia tỷ lệ
\(m\) = số bước trong phép gần đúng tổng Riemann của tích phân
\((x_{i}-x'_{i})\) = một thuật ngữ chỉ sự khác biệt so với đường cơ sở. Điều này là cần thiết để chia tỷ lệ các gradient tích hợp và giữ chúng trong điều kiện của hình ảnh gốc. Đường dẫn từ hình ảnh đường cơ sở đến đầu vào nằm trong không gian pixel. Vì với IG, bạn đang tích phân theo một đường thẳng (phép biến đổi tuyến tính), điều này kết thúc gần tương đương với số hạng tích phân của đạo hàm của hàm hình ảnh nội suy đối với \(\alpha\) với đủ các bước. Tích phân tính tổng gradient của mỗi pixel nhân với sự thay đổi của pixel dọc theo đường dẫn. Việc triển khai tích hợp này đơn giản hơn dưới dạng các bước đồng nhất từ hình ảnh này sang hình ảnh khác, thay thế \(x := (x' + \alpha(x-x'))\). Vì vậy, sự thay đổi của các biến cho ra \(dx = (x-x')d\alpha\). Số \((x-x')\) là không đổi và được tính vào nhân tử của tích phân.
Nội suy hình ảnh
\(IntegratedGrads^{approx}_{i}(x)::=(x_{i}-x'_{i})\times\sum_{k=1}^{m}\frac{\partial F(\overbrace{x' + \frac{k}{m}\times(x - x')}^\text{interpolate m images at k intervals})}{\partial x_{i} } \times \frac{1}{m}\)
Đầu tiên, bạn sẽ tạo một nội suy tuyến tính giữa đường cơ sở và hình ảnh ban đầu. Bạn có thể coi hình ảnh nội suy là các bước nhỏ trong không gian đặc trưng giữa đường cơ sở và đầu vào của bạn, được biểu thị bằng \(\alpha\) trong phương trình ban đầu.
m_steps=50
alphas = tf.linspace(start=0.0, stop=1.0, num=m_steps+1) # Generate m_steps intervals for integral_approximation() below.
def interpolate_images(baseline,
image,
alphas):
alphas_x = alphas[:, tf.newaxis, tf.newaxis, tf.newaxis]
baseline_x = tf.expand_dims(baseline, axis=0)
input_x = tf.expand_dims(image, axis=0)
delta = input_x - baseline_x
images = baseline_x + alphas_x * delta
return images
Hãy sử dụng hàm trên để tạo hình ảnh nội suy dọc theo một đường tuyến tính theo khoảng alpha giữa hình ảnh đường cơ sở màu đen và hình ảnh ví dụ "Fireboat".
interpolated_images = interpolate_images(
baseline=baseline,
image=img_name_tensors['Fireboat'],
alphas=alphas)
Hãy hình dung các hình ảnh nội suy. Lưu ý: một cách nghĩ khác về hằng số \(\alpha\) là nó luôn tăng cường độ của mỗi hình ảnh nội suy một cách nhất quán.
fig = plt.figure(figsize=(20, 20))
i = 0
for alpha, image in zip(alphas[0::10], interpolated_images[0::10]):
i += 1
plt.subplot(1, len(alphas[0::10]), i)
plt.title(f'alpha: {alpha:.1f}')
plt.imshow(image)
plt.axis('off')
plt.tight_layout();
Tính toán độ dốc
Bây giờ chúng ta hãy xem cách tính toán độ dốc để đo lường mối quan hệ giữa những thay đổi đối với một đối tượng địa lý và những thay đổi trong dự đoán của mô hình. Trong trường hợp hình ảnh, gradient cho chúng ta biết pixel nào có ảnh hưởng mạnh nhất đến xác suất lớp được dự đoán của mô hình.
\(IntegratedGrads^{approx}_{i}(x)::=(x_{i}-x'_{i})\times\sum_{k=1}^{m}\frac{\overbrace{\partial F(\text{interpolated images})}^\text{compute gradients} }{\partial x_{i} } \times \frac{1}{m}\)
ở đâu:
\(F()\) = chức năng dự đoán mô hình của bạn
\(\frac{\partial{F} }{\partial{x_i} }\) = gradient (vectơ của các đạo hàm riêng \(\partial\)) của hàm dự đoán mô hình F của bạn so với từng tính năng \(x_i\)
TensorFlow giúp bạn dễ dàng chuyển đổi độ phân giải tính toán với tf.GradientTape
.
def compute_gradients(images, target_class_idx):
with tf.GradientTape() as tape:
tape.watch(images)
logits = model(images)
probs = tf.nn.softmax(logits, axis=-1)[:, target_class_idx]
return tape.gradient(probs, images)
Hãy tính toán độ dốc cho mỗi hình ảnh dọc theo đường nội suy đối với đầu ra chính xác. Nhớ lại rằng mô hình của bạn trả về Tensor
có hình dạng (1, 1001)
với các log mà bạn chuyển đổi thành xác suất dự đoán cho mỗi lớp. Bạn cần chuyển chỉ mục lớp đích ImageNet chính xác cho hàm compute_gradients
cho hình ảnh của bạn.
path_gradients = compute_gradients(
images=interpolated_images,
target_class_idx=555)
Lưu ý hình dạng đầu ra của (n_interpolated_images, img_height, img_width, RGB)
, cung cấp cho chúng ta gradient cho mỗi pixel của mọi hình ảnh dọc theo đường nội suy. Bạn có thể coi những gradient này là đo lường sự thay đổi trong các dự đoán của mô hình của bạn cho mỗi bước nhỏ trong không gian đặc trưng.
print(path_gradients.shape)
(51, 224, 224, 3)
Hình dung độ bão hòa gradient
Nhớ lại rằng các độ dốc mà bạn vừa tính toán ở trên mô tả những thay đổi cục bộ đối với xác suất dự đoán của mô hình của bạn là "Tàu cứu hỏa" và có thể bão hòa .
Các khái niệm này được hình dung bằng cách sử dụng các gradient mà bạn đã tính toán ở trên trong 2 biểu đồ bên dưới.
pred = model(interpolated_images)
pred_proba = tf.nn.softmax(pred, axis=-1)[:, 555]
plt.figure(figsize=(10, 4))
ax1 = plt.subplot(1, 2, 1)
ax1.plot(alphas, pred_proba)
ax1.set_title('Target class predicted probability over alpha')
ax1.set_ylabel('model p(target class)')
ax1.set_xlabel('alpha')
ax1.set_ylim([0, 1])
ax2 = plt.subplot(1, 2, 2)
# Average across interpolation steps
average_grads = tf.reduce_mean(path_gradients, axis=[1, 2, 3])
# Normalize gradients to 0 to 1 scale. E.g. (x - min(x))/(max(x)-min(x))
average_grads_norm = (average_grads-tf.math.reduce_min(average_grads))/(tf.math.reduce_max(average_grads)-tf.reduce_min(average_grads))
ax2.plot(alphas, average_grads_norm)
ax2.set_title('Average pixel gradients (normalized) over alpha')
ax2.set_ylabel('Average pixel gradients')
ax2.set_xlabel('alpha')
ax2.set_ylim([0, 1]);
left : Biểu đồ này cho thấy mức độ tin cậy của mô hình của bạn trong lớp "Fireboat" khác nhau như thế nào giữa các alpha. Chú ý độ dốc, hoặc độ dốc của đường, phần lớn phẳng hoặc bão hòa trong khoảng 0,6 đến 1,0 trước khi ổn định ở xác suất dự đoán cuối cùng của "Tàu cứu hỏa" là khoảng 40%.
đúng : Biểu đồ bên phải hiển thị các độ dốc trung bình lớn hơn alpha một cách trực tiếp hơn. Lưu ý cách các giá trị tiếp cận mạnh và thậm chí giảm xuống dưới 0 một thời gian ngắn. Trên thực tế, mô hình của bạn "học" nhiều nhất từ độ dốc ở các giá trị thấp hơn của alpha trước khi bão hòa. Theo trực giác, bạn có thể nghĩ về điều này vì mô hình của bạn đã học các pixel, ví dụ vòi rồng để đưa ra dự đoán chính xác, gửi các pixel này gradient về 0, nhưng vẫn khá không chắc chắn và tập trung vào các pixel cầu giả hoặc tia nước khi các giá trị alpha tiếp cận với hình ảnh đầu vào gốc.
Để đảm bảo các pixel vòi rồng quan trọng này được phản ánh là quan trọng đối với dự đoán "Fireboat", bạn sẽ tiếp tục ở phần bên dưới để tìm hiểu cách tích lũy các gradient này để ước tính chính xác cách mỗi pixel tác động đến xác suất dự đoán "Fireboat" của bạn.
Tích lũy gradient (xấp xỉ tích phân)
Có nhiều cách khác nhau mà bạn có thể sử dụng để tính toán xấp xỉ số của một tích phân cho IG với sự cân bằng khác nhau về độ chính xác và sự hội tụ trên các hàm khác nhau. Một lớp phổ biến của các phương thức được gọi là tổng Riemann . Ở đây, bạn sẽ sử dụng quy tắc Hình thang (bạn có thể tìm thấy mã bổ sung để khám phá các phương pháp xấp xỉ khác nhau ở cuối hướng dẫn này).
$ IntegratedGrads ^ {xấp xỉ} {i} (x) :: = (x {i} -x ' {i}) \ times \ overbrace {\ sum {k = 1} ^ {m}} ^ \ text {Tổng m gradient cục bộ} \ text {gradient (hình ảnh nội suy)} \ times \ overbrace {\ frac {1} {m}} ^ \ text {Chia cho m bước} $
Từ phương trình, bạn có thể thấy bạn đang tính tổng của m
gradient và chia cho m
bước. Bạn có thể thực hiện hai hoạt động cùng nhau cho phần 3 dưới dạng giá trị trung bình của các gradient cục bộ của m
dự đoán nội suy và hình ảnh đầu vào .
def integral_approximation(gradients):
# riemann_trapezoidal
grads = (gradients[:-1] + gradients[1:]) / tf.constant(2.0)
integrated_gradients = tf.math.reduce_mean(grads, axis=0)
return integrated_gradients
Hàm integral_approximation
cận lấy các độ dốc của xác suất dự đoán của lớp mục tiêu đối với các hình ảnh nội suy giữa đường cơ sở và hình ảnh gốc.
ig = integral_approximation(
gradients=path_gradients)
Bạn có thể xác nhận tính trung bình trên các gradient của m
hình ảnh được nội suy trả về một tensor chuyển sắc tích hợp có cùng hình dạng với hình ảnh "Gấu trúc khổng lồ" ban đầu.
print(ig.shape)
(224, 224, 3)
Để tất cả chúng cùng nhau
Bây giờ, bạn sẽ kết hợp 3 phần chung trước đó với nhau thành một hàm IntegratedGradients
và sử dụng trình trang trí @ tf. function để biên dịch nó thành một đồ thị TensorFlow có thể gọi hiệu suất cao. Điều này được thực hiện như 5 bước nhỏ hơn bên dưới:
\(IntegratedGrads^{approx}_{i}(x)::=\overbrace{(x_{i}-x'_{i})}^\text{5.}\times \overbrace{\sum_{k=1}^{m} }^\text{4.} \frac{\partial \overbrace{F(\overbrace{x' + \overbrace{\frac{k}{m} }^\text{1.}\times(x - x'))}^\text{2.} }^\text{3.} }{\partial x_{i} } \times \overbrace{\frac{1}{m} }^\text{4.}\)
Tạo alphas \(\alpha\)
Tạo hình ảnh nội suy = \((x' + \frac{k}{m}\times(x - x'))\)
Tính toán độ dốc giữa các dự đoán đầu ra của mô hình \(F\) đối với các tính năng đầu vào = \(\frac{\partial F(\text{interpolated path inputs})}{\partial x_{i} }\)
Xấp xỉ tích phân thông qua gradient trung bình = \(\sum_{k=1}^m \text{gradients} \times \frac{1}{m}\)
Chia tỷ lệ gradient tích hợp đối với hình ảnh gốc = \((x_{i}-x'_{i}) \times \text{integrated gradients}\). Lý do bước này là cần thiết là để đảm bảo rằng các giá trị phân bổ được tích lũy trên nhiều hình ảnh nội suy có cùng đơn vị và thể hiện trung thực các điểm nhập pixel trên hình ảnh gốc.
def integrated_gradients(baseline,
image,
target_class_idx,
m_steps=50,
batch_size=32):
# Generate alphas.
alphas = tf.linspace(start=0.0, stop=1.0, num=m_steps+1)
# Collect gradients.
gradient_batches = []
# Iterate alphas range and batch computation for speed, memory efficiency, and scaling to larger m_steps.
for alpha in tf.range(0, len(alphas), batch_size):
from_ = alpha
to = tf.minimum(from_ + batch_size, len(alphas))
alpha_batch = alphas[from_:to]
gradient_batch = one_batch(baseline, image, alpha_batch, target_class_idx)
gradient_batches.append(gradient_batch)
# Stack path gradients together row-wise into single tensor.
total_gradients = tf.stack(gradient_batch)
# Integral approximation through averaging gradients.
avg_gradients = integral_approximation(gradients=total_gradients)
# Scale integrated gradients with respect to input.
integrated_gradients = (image - baseline) * avg_gradients
return integrated_gradients
@tf.function
def one_batch(baseline, image, alpha_batch, target_class_idx):
# Generate interpolated inputs between baseline and input.
interpolated_path_input_batch = interpolate_images(baseline=baseline,
image=image,
alphas=alpha_batch)
# Compute gradients between model outputs and interpolated inputs.
gradient_batch = compute_gradients(images=interpolated_path_input_batch,
target_class_idx=target_class_idx)
return gradient_batch
ig_attributions = integrated_gradients(baseline=baseline,
image=img_name_tensors['Fireboat'],
target_class_idx=555,
m_steps=240)
Một lần nữa, bạn có thể kiểm tra xem các thuộc tính của tính năng IG có cùng hình dạng với hình ảnh "Fireboat" đầu vào hay không.
print(ig_attributions.shape)
(224, 224, 3)
Bài báo đề xuất số lượng các bước nằm trong khoảng từ 20 đến 300 tùy thuộc vào ví dụ (mặc dù trong thực tế, con số này có thể cao hơn trong những năm 1.000 để tính gần đúng chính xác tích phân). Bạn có thể tìm mã bổ sung để kiểm tra số lượng bước thích hợp trong tài nguyên "Các bước tiếp theo" ở cuối hướng dẫn này.
Hình dung các ghi nhận
Bạn đã sẵn sàng để trực quan hóa các thuộc tính và phủ chúng lên hình ảnh gốc. Đoạn mã dưới đây tổng hợp các giá trị tuyệt đối của các chuyển màu được tích hợp trên các kênh màu để tạo ra một mặt nạ phân bổ. Phương pháp vẽ biểu đồ này ghi lại tác động tương đối của các pixel đối với các dự đoán của mô hình.
def plot_img_attributions(baseline,
image,
target_class_idx,
m_steps=50,
cmap=None,
overlay_alpha=0.4):
attributions = integrated_gradients(baseline=baseline,
image=image,
target_class_idx=target_class_idx,
m_steps=m_steps)
# Sum of the attributions across color channels for visualization.
# The attribution mask shape is a grayscale image with height and width
# equal to the original image.
attribution_mask = tf.reduce_sum(tf.math.abs(attributions), axis=-1)
fig, axs = plt.subplots(nrows=2, ncols=2, squeeze=False, figsize=(8, 8))
axs[0, 0].set_title('Baseline image')
axs[0, 0].imshow(baseline)
axs[0, 0].axis('off')
axs[0, 1].set_title('Original image')
axs[0, 1].imshow(image)
axs[0, 1].axis('off')
axs[1, 0].set_title('Attribution mask')
axs[1, 0].imshow(attribution_mask, cmap=cmap)
axs[1, 0].axis('off')
axs[1, 1].set_title('Overlay')
axs[1, 1].imshow(attribution_mask, cmap=cmap)
axs[1, 1].imshow(image, alpha=overlay_alpha)
axs[1, 1].axis('off')
plt.tight_layout()
return fig
Nhìn vào các ghi chú trên hình ảnh "Fireboat", bạn có thể thấy mô hình xác định vòi rồng và vòi rồng góp phần vào dự đoán chính xác của nó.
_ = plot_img_attributions(image=img_name_tensors['Fireboat'],
baseline=baseline,
target_class_idx=555,
m_steps=240,
cmap=plt.cm.inferno,
overlay_alpha=0.4)
Trên hình ảnh "Gấu trúc khổng lồ", các dấu hiệu nhận biết làm nổi bật kết cấu, mũi và bộ lông trên khuôn mặt của Gấu trúc.
_ = plot_img_attributions(image=img_name_tensors['Giant Panda'],
baseline=baseline,
target_class_idx=389,
m_steps=55,
cmap=plt.cm.viridis,
overlay_alpha=0.5)
Sử dụng và hạn chế
Trường hợp sử dụng
- Sử dụng các kỹ thuật như Tích hợp Gradients trước khi triển khai mô hình của bạn có thể giúp bạn phát triển trực giác về cách thức và lý do nó hoạt động. Các đặc điểm được đánh dấu bằng kỹ thuật này có phù hợp với trực giác của bạn không? Nếu không, đó có thể là dấu hiệu của lỗi trong mô hình hoặc tập dữ liệu của bạn hoặc trang bị quá mức.
Hạn chế
Gradients tích hợp cung cấp các nhập tính năng trên các ví dụ riêng lẻ, tuy nhiên, nó không cung cấp các nhập tính năng toàn cầu trên toàn bộ tập dữ liệu.
Gradients tích hợp cung cấp các nhập tính năng riêng lẻ, nhưng nó không giải thích các tương tác và kết hợp tính năng.
Bước tiếp theo
Hướng dẫn này trình bày cách triển khai cơ bản của Tích hợp Gradients. Bước tiếp theo, bạn có thể sử dụng sổ tay này để tự mình thử kỹ thuật này với các mô hình và hình ảnh khác nhau.
Đối với những độc giả quan tâm, có một phiên bản dài hơn của hướng dẫn này (bao gồm mã cho các đường cơ sở khác nhau, để tính toán xấp xỉ tích phân và xác định đủ số bước) mà bạn có thể tìm thấy tại đây .
Để hiểu sâu hơn, hãy xem tài liệu Axiomatic Attribution cho Deep Networks và kho lưu trữ Github , nơi chứa cách triển khai trong phiên bản trước của TensorFlow. Bạn cũng có thể khám phá phân bổ tính năng và tác động của các đường cơ sở khác nhau, trên chưng cất.pub .
Bạn quan tâm đến việc kết hợp IG vào quy trình công việc học máy sản xuất của mình để nhập tính năng, phân tích lỗi mô hình và theo dõi độ lệch dữ liệu? Hãy xem sản phẩm AI có thể giải thích của Google Cloud hỗ trợ phân bổ IG. Nhóm nghiên cứu AI PAIR của Google cũng tạo nguồn mở cho công cụ What-if có thể được sử dụng để gỡ lỗi mô hình, bao gồm cả việc trực quan hóa các thuộc tính của tính năng IG.