ดูบน TensorFlow.org | ทำงานใน Google Colab | ดูบน GitHub | ดาวน์โหลดโน๊ตบุ๊ค | ดูรุ่น TF Hub |
บทช่วยสอนนี้สาธิตวิธีการใช้ Integrated Gradients (IG) ซึ่งเป็นเทคนิค AI ที่อธิบายได้ ซึ่งแนะนำในเอกสาร Axiomatic Attribution สำหรับ Deep Networks IG มีวัตถุประสงค์เพื่ออธิบายความสัมพันธ์ระหว่างการคาดการณ์ของโมเดลในแง่ของคุณลักษณะ มีกรณีการใช้งานมากมาย รวมถึงการทำความเข้าใจถึงความสำคัญของฟีเจอร์ การระบุความเอียงของข้อมูล และการดีบักประสิทธิภาพของโมเดล
IG กลายเป็นเทคนิคการตีความที่ได้รับความนิยมเนื่องจากมีการนำไปใช้ในวงกว้างกับแบบจำลองที่แตกต่างกัน (เช่น รูปภาพ ข้อความ ข้อมูลที่มีโครงสร้าง) ความง่ายในการนำไปใช้ การให้เหตุผลทางทฤษฎี และประสิทธิภาพในการคำนวณเมื่อเทียบกับวิธีการทางเลือกที่ช่วยให้สามารถปรับขนาดเป็นเครือข่ายขนาดใหญ่และคุณลักษณะ ช่องว่างเช่นภาพ
ในบทช่วยสอนนี้ คุณจะอธิบายการใช้งาน IG ทีละขั้นตอนเพื่อทำความเข้าใจความสำคัญของฟีเจอร์พิกเซลของตัวแยกประเภทรูปภาพ ตัวอย่างเช่น ลองพิจารณา ภาพ ของเรือดับเพลิงที่พ่นละอองน้ำ คุณจะจัดรูปภาพนี้เป็นเรือดับเพลิง และอาจเน้นพิกเซลที่ประกอบเป็นเรือและปืนฉีดน้ำว่ามีความสำคัญต่อการตัดสินใจของคุณ โมเดลของคุณจะจัดประเภทรูปภาพนี้เป็นเรือดับเพลิงในภายหลังในบทช่วยสอนนี้ อย่างไรก็ตาม มันเน้นพิกเซลเดียวกันว่ามีความสำคัญหรือไม่เมื่ออธิบายการตัดสินใจ
ในภาพด้านล่างชื่อ "IG Attribution Mask" และ "Original + IG Mask Overlay" คุณจะเห็นว่าโมเดลของคุณไฮไลต์ (สีม่วง) แทนพิกเซลที่ประกอบด้วยปืนใหญ่ฉีดน้ำของเรือและกระแสน้ำที่พุ่งออกมาว่ามีความสำคัญมากกว่าตัวเรือเอง การตัดสินใจของมัน แบบจำลองของคุณจะมีลักษณะทั่วไปอย่างไรกับเรือดับเพลิงใหม่ แล้วเรือดับเพลิงที่ไม่มีเครื่องฉีดน้ำล่ะ? อ่านต่อไปเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับวิธีการทำงานของ IG และวิธีการใช้ IG กับแบบจำลองของคุณเพื่อทำความเข้าใจความสัมพันธ์ระหว่างการคาดคะเนและคุณสมบัติพื้นฐานได้ดียิ่งขึ้น
ติดตั้ง
import matplotlib.pylab as plt
import numpy as np
import tensorflow as tf
import tensorflow_hub as hub
ดาวน์โหลดตัวแยกประเภทรูปภาพที่ผ่านการฝึกอบรมจาก TF-Hub
IG ใช้ได้กับโมเดลดิฟเฟอเรนติเอเบิลใดก็ได้ ตามเจตนารมณ์ของเอกสารต้นฉบับ คุณจะใช้ Inception V1 รุ่นก่อนการฝึกอบรม ซึ่งคุณจะดาวน์โหลดจาก 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 _________________________________________________________________
จากหน้าโมดูล คุณต้องคำนึงถึงสิ่งต่อไปนี้เกี่ยวกับ Inception V1:
อินพุต : รูปร่างอินพุตที่คาดไว้สำหรับโมเดลคือ (None, 224, 224, 3)
นี่คือเทนเซอร์ 4D หนาแน่นของ dtype float32 และรูปร่าง (batch_size, height, width, RGB channels)
ซึ่งมีองค์ประกอบเป็นค่าสี RGB ของพิกเซลที่ปรับให้เป็นมาตรฐานในช่วง [0, 1] อิลิเมนต์แรกคือ None
เพื่อระบุว่าโมเดลสามารถใช้ขนาดแบตช์จำนวนเต็มใดๆ ก็ได้
ผลลัพธ์ : tf.Tensor
ของ logits ในรูปของ (batch_size, 1001)
แต่ละแถวแสดงถึงคะแนนที่คาดการณ์ไว้ของโมเดลสำหรับแต่ละคลาส 1,001 จาก ImageNet สำหรับดัชนีคลาสที่คาดการณ์สูงสุดของโมเดล คุณสามารถใช้ tf.argmax(predictions, axis=-1)
นอกจากนี้ คุณยังสามารถแปลงเอาต์พุตบันทึกของโมเดลเป็นความน่าจะเป็นที่คาดการณ์ได้ในทุกคลาสโดยใช้ tf.nn.softmax(predictions, axis=-1)
เพื่อหาปริมาณความไม่แน่นอนของโมเดล ตลอดจนสำรวจคลาสที่คาดการณ์ไว้ที่คล้ายกันสำหรับการดีบัก
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')
ตัวยึดตำแหน่ง36 โหลดและประมวลผลภาพล่วงหน้าด้วย tf.image
คุณจะแสดง IG โดยใช้สองภาพจาก Wikimedia Commons : Fireboat และ Giant Panda
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()
จำแนกภาพ
มาเริ่มด้วยการจำแนกภาพเหล่านี้และแสดงการคาดคะเนที่มั่นใจที่สุด 3 อันดับแรก ต่อไปนี้เป็นฟังก์ชันยูทิลิตี้เพื่อดึงป้ายกำกับและความน่าจะเป็นที่คาดการณ์ไว้ด้านบน
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%}')
ตัวยึดตำแหน่ง42 fireboat: 32.6% pier: 12.7% suspension bridge: 5.7%
giant panda: 89.4% teddy: 0.3% gibbon: 0.3%
คำนวณการไล่ระดับสีแบบบูรณาการ
โมเดลของคุณ Inception V1 เป็นฟังก์ชันที่เรียนรู้ซึ่งอธิบายการแมประหว่างพื้นที่คุณสมบัติอินพุต ค่าพิกเซลของภาพ และพื้นที่เอาต์พุตที่กำหนดโดยค่าความน่าจะเป็นของคลาส ImageNet ระหว่าง 0 ถึง 1 วิธีการตีความเบื้องต้นสำหรับเครือข่ายประสาทเทียมที่กำหนดคะแนนความสำคัญของฟีเจอร์โดยใช้ การไล่ระดับสี ซึ่งจะบอกคุณว่าพิกเซลใดมีความชันมากที่สุดเมื่อเทียบกับการคาดการณ์ของโมเดลของคุณ ณ จุดที่กำหนดตามฟังก์ชันการคาดการณ์ของโมเดลของคุณ อย่างไรก็ตาม การไล่ระดับสีจะอธิบาย เฉพาะ การเปลี่ยนแปลงในฟังก์ชันการคาดคะเนของแบบจำลองของคุณตามค่าพิกเซล และไม่ได้อธิบายฟังก์ชันการคาดการณ์แบบจำลองทั้งหมดของคุณทั้งหมด เนื่องจากโมเดลของคุณ "เรียนรู้" ความสัมพันธ์ระหว่างช่วงของพิกเซลแต่ละพิกเซลและคลาส ImageNet ที่ถูกต้องอย่างเต็มที่ การไล่ระดับสีสำหรับพิกเซลนี้จะ อิ่มตัว ซึ่งหมายความว่าจะเล็กลงเรื่อยๆ และอาจถึงศูนย์ พิจารณาฟังก์ชันโมเดลอย่างง่ายด้านล่าง:
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();
ซ้าย : การไล่ระดับสีของโมเดลของคุณสำหรับพิกเซล
x
เป็นค่าบวกระหว่าง 0.0 ถึง 0.8 แต่ไปที่ 0.0 ระหว่าง 0.8 ถึง 1.0 Pixelx
มีผลกระทบอย่างมากต่อการผลักดันโมเดลของคุณไปสู่ความน่าจะเป็นที่คาดการณ์ไว้ 80% ในคลาสจริง มันสมเหตุสมผลหรือไม่ที่ความสำคัญของ pixelx
นั้นเล็กหรือไม่ต่อเนื่อง?right : สัญชาตญาณเบื้องหลัง IG คือการสะสมการไล่ระดับสีในเครื่องของ pixel
x
และระบุความสำคัญของมันเป็นคะแนนสำหรับความน่าจะเป็นของคลาสเอาต์พุตโดยรวมของโมเดลที่เพิ่มหรือลบออก คุณสามารถแยกย่อยและคำนวณ IG ได้ 3 ส่วน:- สอดแทรกขั้นตอนเล็กๆ ตามแนวเส้นตรงในช่องว่างระหว่าง 0 (เส้นฐานหรือจุดเริ่มต้น) และ 1 (ค่าพิกเซลของอินพุต)
- คำนวณการไล่ระดับสีในแต่ละขั้นตอนระหว่างการคาดคะเนของแบบจำลองของคุณตามแต่ละขั้นตอน
- ประมาณค่าอินทิกรัลระหว่างเส้นพื้นฐานและอินพุตของคุณโดยการสะสม (ค่าเฉลี่ยสะสม) การไล่ระดับสีในพื้นที่เหล่านี้
เพื่อตอกย้ำสัญชาตญาณนี้ คุณจะต้องเดินผ่าน 3 ส่วนนี้โดยใช้ IG กับภาพตัวอย่าง "Fireboat" ด้านล่าง
วางรากฐาน
ข้อมูลพื้นฐานคือรูปภาพอินพุตที่ใช้เป็นจุดเริ่มต้นในการคำนวณความสำคัญของคุณลักษณะ ตามสัญชาตญาณ คุณสามารถนึกถึงบทบาทอธิบายพื้นฐานของเส้นฐานว่าเป็นตัวแทนผลกระทบของการไม่มีแต่ละพิกเซลในการทำนาย "เรือไฟ" เพื่อเปรียบเทียบกับผลกระทบของแต่ละพิกเซลในการทำนาย "เรือไฟ" เมื่อปรากฏในภาพที่นำเข้า ด้วยเหตุนี้ การเลือกเส้นฐานจึงมีบทบาทสำคัญในการตีความและการแสดงภาพความสำคัญของคุณลักษณะพิกเซล สำหรับการอภิปรายเพิ่มเติมเกี่ยวกับการเลือกพื้นฐาน โปรดดูแหล่งข้อมูลในส่วน "ขั้นตอนถัดไป" ที่ด้านล่างของบทช่วยสอนนี้ ที่นี่ คุณจะใช้ภาพสีดำที่มีค่าพิกเซลเป็นศูนย์ทั้งหมด
ตัวเลือกอื่นๆ ที่คุณสามารถทดลองได้ ได้แก่ รูปภาพสีขาวทั้งหมด หรือรูปภาพสุ่ม ซึ่งคุณสามารถสร้างด้วย 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()
แกะสูตรเป็นโค้ด
สูตรสำหรับ Integrated Gradients มีดังนี้:
\(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}\)
ที่ไหน:
\(_{i}\) = คุณสมบัติ
\(x\) = อินพุต
\(x'\) = พื้นฐาน
\(\alpha\) = ค่าคงที่การแก้ไขเพื่อรบกวนคุณสมบัติโดย
ในทางปฏิบัติ การคำนวณอินทิกรัลที่แน่นอนอาจไม่ใช่ตัวเลขเสมอไปและอาจมีค่าใช้จ่ายสูงในการคำนวณ ดังนั้น คุณจึงคำนวณค่าประมาณเชิงตัวเลขต่อไปนี้:
\(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}\)
ที่ไหน:
\(_{i}\) = คุณลักษณะ (แต่ละพิกเซล)
\(x\) = อินพุต (ภาพเทนเซอร์)
\(x'\) = พื้นฐาน (ภาพเทนเซอร์)
\(k\) = ค่าคงที่การรบกวนคุณสมบัติที่ปรับขนาด
\(m\) = จำนวนขั้นตอนในการประมาณผลรวมของรีมันน์ของอินทิกรัล
\((x_{i}-x'_{i})\) = คำศัพท์สำหรับความแตกต่างจากการตรวจวัดพื้นฐาน นี่เป็นสิ่งจำเป็นในการปรับขนาดการไล่ระดับสีแบบรวมและเก็บไว้ในรูปต้นฉบับ เส้นทางจากภาพพื้นฐานไปยังอินพุตอยู่ในพื้นที่พิกเซล เนื่องจาก IG คุณกำลังรวมเข้าด้วยกันเป็นเส้นตรง (การแปลงเชิงเส้น) จึงเทียบเท่ากับระยะอินทิกรัลของอนุพันธ์ของฟังก์ชันภาพที่สอดแทรกด้วยความเคารพ \(\alpha\) ด้วยขั้นตอนที่เพียงพอ อินทิกรัลรวมการไล่ระดับสีของแต่ละพิกเซลคูณด้วยการเปลี่ยนแปลงในพิกเซลตามเส้นทาง ง่ายกว่าที่จะใช้การรวมนี้เป็นขั้นตอนเดียวกันจากภาพหนึ่งไปอีกภาพหนึ่ง โดยแทนที่ \(x := (x' + \alpha(x-x'))\)ดังนั้นการเปลี่ยนแปลงของตัวแปรจึงทำให้ \(dx = (x-x')d\alpha\)\((x-x')\) เทอมเป็นค่าคงที่และแยกตัวประกอบจากอินทิกรัล
สอดแทรกภาพ
\(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}\)
ขั้นแรก คุณจะต้องสร้างการประมาณค่า เชิงเส้น ระหว่างเส้นฐานและรูปภาพต้นฉบับ คุณสามารถนึกถึงภาพที่สอดแทรกเป็นขั้นตอนเล็กๆ ในช่องว่างระหว่างเส้นฐานและอินพุตของคุณ ซึ่งแสดงโดย \(\alpha\) ในสมการดั้งเดิม
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
ลองใช้ฟังก์ชันด้านบนเพื่อสร้างภาพที่สอดแทรกตามเส้นทางเชิงเส้นที่ช่วงอัลฟาระหว่างภาพเส้นฐานสีดำกับภาพตัวอย่าง "เรือไฟ"
interpolated_images = interpolate_images(
baseline=baseline,
image=img_name_tensors['Fireboat'],
alphas=alphas)
มาดูภาพที่สอดแทรกกัน หมายเหตุ: วิธีคิดอีกวิธีหนึ่งเกี่ยวกับค่าคงที่ \(\alpha\) คือการเพิ่มความเข้มของภาพที่สอดแทรกแต่ละภาพอย่างต่อเนื่อง
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();
คำนวณการไล่ระดับสี
ตอนนี้ เรามาดูวิธีการคำนวณการไล่ระดับสีเพื่อวัดความสัมพันธ์ระหว่างการเปลี่ยนแปลงคุณลักษณะและการเปลี่ยนแปลงในการคาดคะเนของแบบจำลอง ในกรณีของรูปภาพ การไล่ระดับสีบอกเราว่าพิกเซลใดมีผลมากที่สุดต่อโมเดลที่คาดการณ์ความน่าจะเป็นของคลาส
\(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}\)
ที่ไหน:
\(F()\) = ฟังก์ชันการทำนายแบบจำลองของคุณ
\(\frac{\partial{F} }{\partial{x_i} }\) = การไล่ระดับสี (เวกเตอร์ของอนุพันธ์บางส่วน \(\partial\)) ของฟังก์ชันการคาดการณ์ของแบบจำลอง F ที่สัมพันธ์กับคุณลักษณะแต่ละอย่าง \(x_i\)
TensorFlow ทำให้การไล่ระดับสีในคอมพิวเตอร์เป็นเรื่องง่ายสำหรับคุณด้วย 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)
มาคำนวณการไล่ระดับสีสำหรับแต่ละภาพตามเส้นทางการแก้ไขโดยคำนึงถึงผลลัพธ์ที่ถูกต้อง โปรดจำไว้ว่าโมเดลของคุณส่งคืน Tensor
ที่มีรูปทรง (1, 1001)
พร้อมบันทึกที่คุณแปลงเป็นความน่าจะเป็นที่คาดการณ์ไว้สำหรับแต่ละคลาส คุณต้องส่งดัชนีคลาสเป้าหมายของ ImageNet ที่ถูกต้องไปยังฟังก์ชัน compute_gradients
สำหรับรูปภาพของคุณ
path_gradients = compute_gradients(
images=interpolated_images,
target_class_idx=555)
สังเกตรูปร่างผลลัพธ์ของ (n_interpolated_images, img_height, img_width, RGB)
ซึ่งให้การไล่ระดับสีสำหรับทุกพิกเซลของทุกภาพตามเส้นทางการแก้ไข คุณสามารถนึกถึงการไล่ระดับสีเหล่านี้เป็นการวัดการเปลี่ยนแปลงในการคาดคะเนของแบบจำลองของคุณสำหรับแต่ละขั้นตอนเล็กๆ ในพื้นที่คุณลักษณะ
print(path_gradients.shape)
(51, 224, 224, 3)
การแสดงภาพความอิ่มตัวของการไล่ระดับสี
โปรดจำไว้ว่าการไล่ระดับสีที่คุณเพิ่งคำนวณข้างต้นจะอธิบาย การ เปลี่ยนแปลงในเครื่องต่อความน่าจะเป็นที่คาดการณ์ของโมเดลของคุณที่จะเป็น "เรือไฟ" และสามารถ อิ่มตัว ได้
แนวคิดเหล่านี้แสดงให้เห็นภาพโดยใช้การไล่ระดับสีที่คุณคำนวณไว้ด้านบนใน 2 แปลงด้านล่าง
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]);
ซ้าย : พล็อตนี้แสดงให้เห็นว่าความเชื่อมั่นของโมเดลของคุณในคลาส "Fireboat" นั้นแตกต่างกันอย่างไรในอัลฟ่า สังเกตว่าการไล่ระดับสีหรือความชันของเส้น ส่วนใหญ่แผ่หรืออิ่มตัวระหว่าง 0.6 ถึง 1.0 ก่อนที่จะตกตะกอนที่ "เรือดับเพลิง" สุดท้ายทำนายความน่าจะเป็นประมาณ 40%
ขวา : พล็อตด้านขวาแสดงขนาดการไล่ระดับสีเฉลี่ยเหนืออัลฟาโดยตรงมากขึ้น สังเกตว่าค่าเข้าใกล้อย่างรวดเร็วและลดลงต่ำกว่าศูนย์ในเวลาสั้นๆ อย่างไร อันที่จริง โมเดลของคุณ "เรียนรู้" มากที่สุดจากการไล่ระดับสีที่ค่าอัลฟาที่ต่ำกว่าก่อนที่จะอิ่มตัว ตามสัญชาตญาณ คุณสามารถคิดสิ่งนี้ได้เนื่องจากแบบจำลองของคุณได้เรียนรู้พิกเซล เช่น ปืนฉีดน้ำ ในการทำนายที่ถูกต้อง โดยส่งการไล่ระดับพิกเซลเหล่านี้ไปที่ศูนย์ แต่ก็ยังค่อนข้างไม่แน่นอนและมุ่งเน้นไปที่บริดจ์ปลอมหรือพิกเซลของวอเตอร์เจ็ทเมื่อค่าอัลฟาเข้าใกล้ ภาพอินพุตต้นฉบับ
เพื่อให้แน่ใจว่าพิกเซลของปืนใหญ่ฉีดน้ำที่สำคัญเหล่านี้สะท้อนให้เห็นว่ามีความสำคัญต่อการคาดคะเน "เรือดับเพลิง" คุณจะดำเนินการต่อที่ด้านล่างเพื่อเรียนรู้วิธีรวบรวมการไล่ระดับสีเหล่านี้เพื่อประมาณว่าแต่ละพิกเซลส่งผลต่อความน่าจะเป็นที่คาดการณ์ไว้ของ "เรือดับเพลิง" อย่างไร
สะสมการไล่ระดับสี (การประมาณแบบอินทิกรัล)
มีหลายวิธีที่คุณสามารถคำนวณค่าประมาณเชิงตัวเลขของอินทิกรัลสำหรับ IG ได้โดยมีจุดประนีประนอมที่แตกต่างกันในด้านความแม่นยำและการบรรจบกันในฟังก์ชันต่างๆ คลาสวิธีการที่นิยมเรียกว่า Riemann sums ที่นี่ คุณจะใช้กฎสี่เหลี่ยมคางหมู (คุณสามารถหาโค้ดเพิ่มเติมเพื่อสำรวจวิธีการประมาณต่างๆ ได้ที่ส่วนท้ายของบทช่วยสอนนี้)
$IntegratedGrads^{ประมาณ} {i}(x)::=(x {i}-x' {i})\times \overbrace{\sum {k=1}^{m} }^\text{Sum m การไล่ระดับสีในพื้นที่} \text{gradients(interpolated images)} \times \overbrace{\frac{1}{m} }^\text{หารด้วย m ขั้นตอน}$
จากสมการ คุณจะเห็นว่าคุณกำลังรวมการไล่ระดับสี m
และหารด้วย m
ขั้นตอน คุณสามารถใช้การดำเนินการทั้งสองร่วมกันสำหรับส่วนที่ 3 เป็น ค่าเฉลี่ยของการไล่ระดับสีในพื้นที่ของการคาดคะเน m
และภาพที่ป้อนเข้า
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
ฟังก์ชัน integral_approximation
จะใช้การไล่ระดับสีของความน่าจะเป็นที่คาดการณ์ไว้ของคลาสเป้าหมายโดยสัมพันธ์กับภาพที่สอดแทรกระหว่างเส้นฐานและภาพต้นฉบับ
ig = integral_approximation(
gradients=path_gradients)
คุณสามารถยืนยันการเฉลี่ยจากการไล่ระดับของรูปภาพที่มีการสอดแทรก m
ให้ส่งคืนเทนเซอร์การไล่ระดับสีแบบรวมที่มีรูปร่างเหมือนกับรูปภาพ "Giant Panda" ดั้งเดิมได้
print(ig.shape)
(224, 224, 3)
วางมันทั้งหมดเข้าด้วยกัน
ตอนนี้ คุณจะรวม 3 ส่วนทั่วไปก่อนหน้าเข้าด้วยกันเป็นฟังก์ชัน IntegratedGradients
และใช้ @tf.function decorator เพื่อคอมไพล์ลงในกราฟ TensorFlow ที่เรียกได้ประสิทธิภาพสูง มีการดำเนินการตามขั้นตอนที่เล็กกว่า 5 ขั้นตอนด้านล่าง:
\(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.}\)
สร้าง alphas \(\alpha\)
สร้างภาพที่สอดแทรก = \((x' + \frac{k}{m}\times(x - x'))\)
คำนวณการไล่ระดับสีระหว่างแบบจำลอง \(F\) การคาดการณ์เอาต์พุตที่สัมพันธ์กับคุณสมบัติอินพุต = \(\frac{\partial F(\text{interpolated path inputs})}{\partial x_{i} }\)
การประมาณแบบอินทิกรัลผ่านการไล่ระดับสีเฉลี่ย = \(\sum_{k=1}^m \text{gradients} \times \frac{1}{m}\)
ปรับขนาดการไล่ระดับสีแบบบูรณาการตามภาพต้นฉบับ = \((x_{i}-x'_{i}) \times \text{integrated gradients}\)เหตุผลที่ต้องทำขั้นตอนนี้คือต้องตรวจสอบให้แน่ใจว่าค่าการระบุแหล่งที่มาที่สะสมจากรูปภาพที่มีการสอดแทรกหลายภาพอยู่ในหน่วยเดียวกัน และแสดงถึงความสำคัญของพิกเซลบนรูปภาพต้นฉบับอย่างเที่ยงตรง
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)
อีกครั้ง คุณสามารถตรวจสอบได้ว่าการระบุแหล่งที่มาของฟีเจอร์ IG นั้นมีรูปร่างเหมือนกันกับอิมเมจ "Fireboat" ที่ป้อน
print(ig_attributions.shape)
(224, 224, 3)
กระดาษนี้แนะนำจำนวนขั้นตอนในช่วงระหว่าง 20 ถึง 300 ขึ้นอยู่กับตัวอย่าง (แม้ว่าในทางปฏิบัติ ขั้นตอนนี้อาจสูงกว่าใน 1,000 วินาทีเพื่อประมาณค่าปริพันธ์ได้อย่างแม่นยำ) คุณสามารถค้นหาโค้ดเพิ่มเติมเพื่อตรวจสอบจำนวนขั้นตอนที่เหมาะสมได้ในแหล่งข้อมูล "ขั้นตอนถัดไป" ที่ส่วนท้ายของบทช่วยสอนนี้
เห็นภาพการแสดงที่มา
คุณพร้อมที่จะเห็นภาพการแสดงที่มาและซ้อนทับบนภาพต้นฉบับ โค้ดด้านล่างจะรวมค่าสัมบูรณ์ของการไล่ระดับสีแบบรวมในช่องสีต่างๆ เพื่อสร้างรูปแบบการระบุแหล่งที่มา วิธีการวางแผนนี้จะจับผลกระทบที่สัมพันธ์กันของพิกเซลต่อการคาดคะเนของแบบจำลอง
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
เมื่อพิจารณาจากที่มาของภาพ "เรือดับเพลิง" คุณจะเห็นโมเดลระบุปืนใหญ่ฉีดน้ำและท่อส่งน้ำซึ่งมีส่วนช่วยในการทำนายที่ถูกต้อง
_ = 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)
ในภาพ "Giant Panda" การระบุแหล่งที่มาจะเน้นที่พื้นผิว จมูก และขนของใบหน้าของแพนด้า
_ = 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)
การใช้และข้อจำกัด
กรณีการใช้งาน
- การใช้เทคนิคต่างๆ เช่น Integrated Gradients ก่อนปรับใช้โมเดลของคุณสามารถช่วยให้คุณพัฒนาสัญชาตญาณว่าทำงานอย่างไรและทำไม คุณลักษณะที่เน้นโดยเทคนิคนี้ตรงกับสัญชาตญาณของคุณหรือไม่? หากไม่เป็นเช่นนั้น นั่นอาจบ่งบอกถึงจุดบกพร่องในแบบจำลองหรือชุดข้อมูลของคุณ หรือการใส่มากเกินไป
ข้อจำกัด
การไล่ระดับสีแบบรวมจะให้ความสำคัญกับคุณลักษณะในแต่ละตัวอย่าง อย่างไรก็ตาม ไม่ได้ให้ความสำคัญกับคุณลักษณะโดยรวมในชุดข้อมูลทั้งหมด
การไล่ระดับสีแบบรวมจะให้ความสำคัญกับคุณลักษณะแต่ละอย่าง แต่ไม่ได้อธิบายการโต้ตอบและการผสมผสานของคุณลักษณะ
ขั้นตอนถัดไป
บทช่วยสอนนี้นำเสนอการใช้งานพื้นฐานของ Integrated Gradients ในขั้นตอนต่อไป คุณสามารถใช้สมุดบันทึกนี้เพื่อลองใช้เทคนิคนี้กับรุ่นและรูปภาพต่างๆ ด้วยตัวคุณเอง
สำหรับผู้อ่านที่สนใจ มีบทช่วยสอนนี้เวอร์ชันที่ยาวกว่า (ซึ่งรวมถึงโค้ดสำหรับบรรทัดฐานที่แตกต่างกัน เพื่อคำนวณค่าประมาณที่สมบูรณ์ และเพื่อกำหนดจำนวนขั้นตอนที่เพียงพอ) ซึ่งคุณจะพบได้ ที่นี่
เพื่อทำความเข้าใจให้ลึกซึ้งยิ่งขึ้น โปรดดูเอกสาร Axiomatic Attribution for Deep Networks และ Github repository ซึ่งมีการใช้งานใน TensorFlow เวอร์ชันก่อนหน้า คุณยังสามารถสำรวจการระบุแหล่งที่มาของคุณลักษณะ และผลกระทบของบรรทัดฐานต่างๆ ได้ที่ distill.pub
สนใจที่จะรวม IG เข้ากับเวิร์กโฟลว์แมชชีนเลิร์นนิงสำหรับการผลิตของคุณเพื่อให้ความสำคัญกับคุณลักษณะ การวิเคราะห์ข้อผิดพลาดของแบบจำลอง และการตรวจสอบข้อมูลเอียงหรือไม่ ลองดูผลิตภัณฑ์ AI ที่อธิบายได้ของ Google Cloud ที่รองรับการระบุแหล่งที่มาของ IG กลุ่มวิจัย Google AI PAIR ยังเปิดแหล่งที่มาของ เครื่องมือ What-if ซึ่งสามารถใช้สำหรับการแก้ไขข้อบกพร่องของโมเดล ซึ่งรวมถึงการแสดงภาพการระบุแหล่งที่มาของคุณลักษณะ IG