การไล่ระดับสีแบบบูรณาการ

บทช่วยสอนนี้สาธิตวิธีการใช้ Integrated Gradients (IG) ซึ่งเป็นเทคนิค AI ที่อธิบายได้ ซึ่งแนะนำในเอกสาร Axiomatic Attribution สำหรับ Deep Networks IG มีวัตถุประสงค์เพื่ออธิบายความสัมพันธ์ระหว่างการคาดการณ์ของโมเดลในแง่ของคุณลักษณะ มีกรณีการใช้งานมากมาย รวมถึงการทำความเข้าใจถึงความสำคัญของฟีเจอร์ การระบุความเอียงของข้อมูล และการดีบักประสิทธิภาพของโมเดล

IG กลายเป็นเทคนิคการตีความที่ได้รับความนิยมเนื่องจากมีการนำไปใช้ในวงกว้างกับแบบจำลองที่แตกต่างกัน (เช่น รูปภาพ ข้อความ ข้อมูลที่มีโครงสร้าง) ความง่ายในการนำไปใช้ การให้เหตุผลทางทฤษฎี และประสิทธิภาพในการคำนวณเมื่อเทียบกับวิธีการทางเลือกที่ช่วยให้สามารถปรับขนาดเป็นเครือข่ายขนาดใหญ่และคุณลักษณะ ช่องว่างเช่นภาพ

ในบทช่วยสอนนี้ คุณจะอธิบายการใช้งาน IG ทีละขั้นตอนเพื่อทำความเข้าใจความสำคัญของฟีเจอร์พิกเซลของตัวแยกประเภทรูปภาพ ตัวอย่างเช่น ลองพิจารณา ภาพ ของเรือดับเพลิงที่พ่นละอองน้ำ คุณจะจัดรูปภาพนี้เป็นเรือดับเพลิง และอาจเน้นพิกเซลที่ประกอบเป็นเรือและปืนฉีดน้ำว่ามีความสำคัญต่อการตัดสินใจของคุณ โมเดลของคุณจะจัดประเภทรูปภาพนี้เป็นเรือดับเพลิงในภายหลังในบทช่วยสอนนี้ อย่างไรก็ตาม มันเน้นพิกเซลเดียวกันว่ามีความสำคัญหรือไม่เมื่ออธิบายการตัดสินใจ

ในภาพด้านล่างชื่อ "IG Attribution Mask" และ "Original + IG Mask Overlay" คุณจะเห็นว่าโมเดลของคุณไฮไลต์ (สีม่วง) แทนพิกเซลที่ประกอบด้วยปืนใหญ่ฉีดน้ำของเรือและกระแสน้ำที่พุ่งออกมาว่ามีความสำคัญมากกว่าตัวเรือเอง การตัดสินใจของมัน แบบจำลองของคุณจะมีลักษณะทั่วไปอย่างไรกับเรือดับเพลิงใหม่ แล้วเรือดับเพลิงที่ไม่มีเครื่องฉีดน้ำล่ะ? อ่านต่อไปเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับวิธีการทำงานของ IG และวิธีการใช้ IG กับแบบจำลองของคุณเพื่อทำความเข้าใจความสัมพันธ์ระหว่างการคาดคะเนและคุณสมบัติพื้นฐานได้ดียิ่งขึ้น

ภาพที่ส่งออก 1

ติดตั้ง

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()

png

จำแนกภาพ

มาเริ่มด้วยการจำแนกภาพเหล่านี้และแสดงการคาดคะเนที่มั่นใจที่สุด 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

png

fireboat: 32.6%
pier: 12.7%
suspension bridge: 5.7%

png

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();

png

  • ซ้าย : การไล่ระดับสีของโมเดลของคุณสำหรับพิกเซล x เป็นค่าบวกระหว่าง 0.0 ถึง 0.8 แต่ไปที่ 0.0 ระหว่าง 0.8 ถึง 1.0 Pixel x มีผลกระทบอย่างมากต่อการผลักดันโมเดลของคุณไปสู่ความน่าจะเป็นที่คาดการณ์ไว้ 80% ในคลาสจริง มันสมเหตุสมผลหรือไม่ที่ความสำคัญของ pixel x นั้นเล็กหรือไม่ต่อเนื่อง?

  • right : สัญชาตญาณเบื้องหลัง IG คือการสะสมการไล่ระดับสีในเครื่องของ pixel x และระบุความสำคัญของมันเป็นคะแนนสำหรับความน่าจะเป็นของคลาสเอาต์พุตโดยรวมของโมเดลที่เพิ่มหรือลบออก คุณสามารถแยกย่อยและคำนวณ IG ได้ 3 ส่วน:

    1. สอดแทรกขั้นตอนเล็กๆ ตามแนวเส้นตรงในช่องว่างระหว่าง 0 (เส้นฐานหรือจุดเริ่มต้น) และ 1 (ค่าพิกเซลของอินพุต)
    2. คำนวณการไล่ระดับสีในแต่ละขั้นตอนระหว่างการคาดคะเนของแบบจำลองของคุณตามแต่ละขั้นตอน
    3. ประมาณค่าอินทิกรัลระหว่างเส้นพื้นฐานและอินพุตของคุณโดยการสะสม (ค่าเฉลี่ยสะสม) การไล่ระดับสีในพื้นที่เหล่านี้

เพื่อตอกย้ำสัญชาตญาณนี้ คุณจะต้องเดินผ่าน 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()

png

แกะสูตรเป็นโค้ด

สูตรสำหรับ Integrated Gradients มีดังนี้:

IntegratedGradientsi(x)::=(xixi)×α=01F(x+α×(xx))xidα

ที่ไหน:

i = คุณสมบัติ
x = อินพุต
x = พื้นฐาน
α = ค่าคงที่การแก้ไขเพื่อรบกวนคุณสมบัติโดย

ในทางปฏิบัติ การคำนวณอินทิกรัลที่แน่นอนอาจไม่ใช่ตัวเลขเสมอไปและอาจมีค่าใช้จ่ายสูงในการคำนวณ ดังนั้น คุณจึงคำนวณค่าประมาณเชิงตัวเลขต่อไปนี้:

IntegratedGradsiapprox(x)::=(xixi)×k=1mF(x+km×(xx))xi×1m

ที่ไหน:

i = คุณลักษณะ (แต่ละพิกเซล)
x = อินพุต (ภาพเทนเซอร์)
x = พื้นฐาน (ภาพเทนเซอร์)
k = ค่าคงที่การรบกวนคุณสมบัติที่ปรับขนาด
m = จำนวนขั้นตอนในการประมาณผลรวมของรีมันน์ของอินทิกรัล
(xixi) = คำศัพท์สำหรับความแตกต่างจากการตรวจวัดพื้นฐาน นี่เป็นสิ่งจำเป็นในการปรับขนาดการไล่ระดับสีแบบรวมและเก็บไว้ในรูปต้นฉบับ เส้นทางจากภาพพื้นฐานไปยังอินพุตอยู่ในพื้นที่พิกเซล เนื่องจาก IG คุณกำลังรวมเข้าด้วยกันเป็นเส้นตรง (การแปลงเชิงเส้น) จึงเทียบเท่ากับระยะอินทิกรัลของอนุพันธ์ของฟังก์ชันภาพที่สอดแทรกด้วยความเคารพ α ด้วยขั้นตอนที่เพียงพอ อินทิกรัลรวมการไล่ระดับสีของแต่ละพิกเซลคูณด้วยการเปลี่ยนแปลงในพิกเซลตามเส้นทาง ง่ายกว่าที่จะใช้การรวมนี้เป็นขั้นตอนเดียวกันจากภาพหนึ่งไปอีกภาพหนึ่ง โดยแทนที่ x:=(x+α(xx))ดังนั้นการเปลี่ยนแปลงของตัวแปรจึงทำให้ dx=(xx)dα(xx) เทอมเป็นค่าคงที่และแยกตัวประกอบจากอินทิกรัล

สอดแทรกภาพ

IntegratedGradsiapprox(x)::=(xixi)×k=1mF(x+km×(xx)interpolate m images at k intervals)xi×1m

ขั้นแรก คุณจะต้องสร้างการประมาณค่า เชิงเส้น ระหว่างเส้นฐานและรูปภาพต้นฉบับ คุณสามารถนึกถึงภาพที่สอดแทรกเป็นขั้นตอนเล็กๆ ในช่องว่างระหว่างเส้นฐานและอินพุตของคุณ ซึ่งแสดงโดย α ในสมการดั้งเดิม

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)

มาดูภาพที่สอดแทรกกัน หมายเหตุ: วิธีคิดอีกวิธีหนึ่งเกี่ยวกับค่าคงที่ α คือการเพิ่มความเข้มของภาพที่สอดแทรกแต่ละภาพอย่างต่อเนื่อง

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();

png

คำนวณการไล่ระดับสี

ตอนนี้ เรามาดูวิธีการคำนวณการไล่ระดับสีเพื่อวัดความสัมพันธ์ระหว่างการเปลี่ยนแปลงคุณลักษณะและการเปลี่ยนแปลงในการคาดคะเนของแบบจำลอง ในกรณีของรูปภาพ การไล่ระดับสีบอกเราว่าพิกเซลใดมีผลมากที่สุดต่อโมเดลที่คาดการณ์ความน่าจะเป็นของคลาส

IntegratedGradsiapprox(x)::=(xixi)×k=1mF(interpolated images)compute gradientsxi×1m

ที่ไหน:
F() = ฟังก์ชันการทำนายแบบจำลองของคุณ
Fxi = การไล่ระดับสี (เวกเตอร์ของอนุพันธ์บางส่วน ) ของฟังก์ชันการคาดการณ์ของแบบจำลอง F ที่สัมพันธ์กับคุณลักษณะแต่ละอย่าง xi

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]);

png

  • ซ้าย : พล็อตนี้แสดงให้เห็นว่าความเชื่อมั่นของโมเดลของคุณในคลาส "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 ขั้นตอนด้านล่าง:

IntegratedGradsiapprox(x)::=(xixi)5.×k=1m4.F(x+km1.×(xx))2.3.xi×1m4.

  1. สร้าง alphas α

  2. สร้างภาพที่สอดแทรก = (x+km×(xx))

  3. คำนวณการไล่ระดับสีระหว่างแบบจำลอง F การคาดการณ์เอาต์พุตที่สัมพันธ์กับคุณสมบัติอินพุต = F(interpolated path inputs)xi

  4. การประมาณแบบอินทิกรัลผ่านการไล่ระดับสีเฉลี่ย = k=1mgradients×1m

  5. ปรับขนาดการไล่ระดับสีแบบบูรณาการตามภาพต้นฉบับ = (xixi)×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)

png

ในภาพ "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)

png

การใช้และข้อจำกัด

กรณีการใช้งาน

  • การใช้เทคนิคต่างๆ เช่น 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