ช่วยปกป้อง Great Barrier Reef กับ TensorFlow บน Kaggle เข้าร่วมท้าทาย

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

ดูบน TensorFlow.org ทำงานใน Google Colab ดูบน GitHub ดาวน์โหลดโน๊ตบุ๊ค ดูรุ่น TF Hub

กวดวิชานี้จะแสดงให้เห็นถึงวิธีการดำเนินการแบบบูรณาการไล่ระดับสี (IG) การ อธิบาย AI เทคนิคแนะนำในกระดาษ ซึ่งเป็นจริงการแสดงที่มาสำหรับเครือข่ายลึก 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 เมตริกซ์ของ float32 และรูปร่าง dtype (batch_size, height, width, RGB channels) ซึ่งเป็นธาตุค่า RGB สีของพิกเซลปกติในช่วง [0, 1] องค์ประกอบแรกคือ None การระบุว่ารูปแบบสามารถใช้ขนาดจำนวนเต็มชุดใด ๆ

เอาท์พุท: เป็น tf.Tensor ของ logits ในรูปของ (batch_size, 1001) แต่ละแถวแสดงถึงคะแนนที่คาดการณ์ไว้ของโมเดลสำหรับแต่ละคลาส 1,001 จาก ImageNet สำหรับดัชนีระดับของแบบจำลองคาดการณ์ด้านบนคุณสามารถใช้ tf.argmax(predictions, axis=-1) นอกจากนี้คุณยังสามารถแปลงผลผลิต logit รูปแบบของความน่าจะเป็นที่จะคาดการณ์ทั่วทุกชั้นใช้ 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')
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt
16384/10484 [==============================================] - 0s 0us/step
24576/10484 [======================================================================] - 0s 0us/step

Load และ preprocess ภาพด้วย tf.image

คุณจะแสดงให้เห็นถึงการใช้สอง IG ภาพจาก วิกิพีเดีย ที่: เพลิง และ หมีแพนด้า

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%}')

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 พิกเซล x ชัดเจนมีผลกระทบอย่างมีนัยสำคัญในการผลักดันรูปแบบของคุณไปยัง 80% น่าจะเป็นที่คาดการณ์ไว้ในระดับที่แท้จริง ไม่ได้ทำให้รู้สึกว่าพิกเซล x 's สำคัญมีขนาดเล็กหรือไม่ต่อเนื่อง?

  • ขวา: สัญชาตญาณที่อยู่เบื้องหลัง IG คือการสะสมพิกเซล x 's ไล่ระดับท้องถิ่นและแอตทริบิวต์ความสำคัญในฐานะคะแนนเท่าไหร่ที่จะเพิ่มหรือลบโดยรวมน่าจะเป็นระดับการส่งออกรูปแบบของคุณ คุณสามารถแยกย่อยและคำนวณ 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 มีดังนี้:

\(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\) = จำนวนของขั้นตอนในผลรวมประมาณ Riemann ของการหนึ่ง
\((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();

png

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

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

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

มาคำนวณการไล่ระดับสีสำหรับแต่ละภาพตามเส้นทางการแก้ไขโดยคำนึงถึงผลลัพธ์ที่ถูกต้อง จำได้ว่ารูปแบบของคุณส่งกลับ (1, 1001) รูป Tensor กับ logits ที่คุณแปลงเป็นความน่าจะเป็นที่คาดการณ์ไว้สำหรับแต่ละชั้นเรียน คุณต้องผ่านดัชนี 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

  • ซ้าย: พล็อตนี้แสดงให้เห็นว่าความเชื่อมั่นของแบบจำลองของคุณใน "เพลิง" ชั้นแตกต่างกันไปทั่ว alphas สังเกตว่าการไล่ระดับสีหรือความชันของเส้น ส่วนใหญ่แผ่หรืออิ่มตัวระหว่าง 0.6 ถึง 1.0 ก่อนที่จะตกตะกอนที่ "เรือดับเพลิง" สุดท้ายทำนายความน่าจะเป็นประมาณ 40%

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

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

สะสมการไล่ระดับสี (การประมาณแบบอินทิกรัล)

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

$ IntegratedGrads ^ {ประมาณ} {i} (x) :: = (x {i} -x'{i}) \ times \ overbrace {\ รวม {k = 1} ^ {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 ภาพหยันส่งกลับไล่ระดับสีแบบบูรณาการเมตริกซ์ที่มีรูปร่างเช่นเดียวกับต้นฉบับภาพ "แพนด้ายักษ์"

print(ig.shape)
(224, 224, 3)

วางมันทั้งหมดเข้าด้วยกัน

ตอนนี้คุณจะรวม 3 ส่วนด้วยกันโดยทั่วไปก่อนหน้านี้เป็น IntegratedGradients ฟังก์ชั่นและใช้ @ tf.function มัณฑนากรที่จะรวบรวมมันกลายเป็นที่มีประสิทธิภาพสูง callable 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.}\)

  1. สร้าง alphas \(\alpha\)

  2. สร้างภาพหยัน = \((x' + \frac{k}{m}\times(x - x'))\)

  3. การไล่ระดับสี Compute ระหว่างรุ่น \(F\) การคาดการณ์การส่งออกที่เกี่ยวกับการป้อนข้อมูลให้บริการ = \(\frac{\partial F(\text{interpolated path inputs})}{\partial x_{i} }\)

  4. ประมาณ Integral ผ่านการไล่ระดับสีเฉลี่ย = \(\sum_{k=1}^m \text{gradients} \times \frac{1}{m}\)

  5. สเกลของการไล่ระดับสีแบบบูรณาการที่เกี่ยวกับภาพต้นฉบับ = \((x_{i}-x'_{i}) \times \text{integrated gradients}\)เหตุผลที่ต้องทำขั้นตอนนี้คือต้องตรวจสอบให้แน่ใจว่าค่าการระบุแหล่งที่มาที่สะสมจากรูปภาพที่มีการสอดแทรกหลายภาพอยู่ในหน่วยเดียวกัน และแสดงถึงความสำคัญของพิกเซลบนรูปภาพต้นฉบับอย่างเที่ยงตรง

@tf.function
def integrated_gradients(baseline,
                         image,
                         target_class_idx,
                         m_steps=50,
                         batch_size=32):
  # 1. Generate alphas.
  alphas = tf.linspace(start=0.0, stop=1.0, num=m_steps+1)

  # Initialize TensorArray outside loop to collect gradients.    
  gradient_batches = tf.TensorArray(tf.float32, size=m_steps+1)

  # 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]

    # 2. Generate interpolated inputs between baseline and input.
    interpolated_path_input_batch = interpolate_images(baseline=baseline,
                                                       image=image,
                                                       alphas=alpha_batch)

    # 3. Compute gradients between model outputs and interpolated inputs.
    gradient_batch = compute_gradients(images=interpolated_path_input_batch,
                                       target_class_idx=target_class_idx)

    # Write batch indices and gradients to extend TensorArray.
    gradient_batches = gradient_batches.scatter(tf.range(from_, to), gradient_batch)    

  # Stack path gradients together row-wise into single tensor.
  total_gradients = gradient_batches.stack()

  # 4. Integral approximation through averaging gradients.
  avg_gradients = integral_approximation(gradients=total_gradients)

  # 5. Scale integrated gradients with respect to input.
  integrated_gradients = (image - baseline) * avg_gradients

  return integrated_gradients
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 ในขั้นตอนต่อไป คุณสามารถใช้สมุดบันทึกนี้เพื่อลองใช้เทคนิคนี้กับรุ่นและรูปภาพต่างๆ ด้วยตัวคุณเอง

สำหรับผู้อ่านที่สนใจมีรุ่น lengthier ของการกวดวิชานี้ (ซึ่งรวมถึงสำหรับเส้นเขตแดนที่แตกต่างกันในการคำนวณประมาณหนึ่งและเพื่อกำหนดจำนวนเพียงพอของขั้นตอน) ซึ่งคุณสามารถหาได้ ที่นี่

ที่จะกระชับความเข้าใจของคุณตรวจสอบกระดาษ ซึ่งเป็นจริงการแสดงที่มาสำหรับเครือข่ายลึก และ พื้นที่เก็บข้อมูล Github ซึ่งมีการดำเนินการในรุ่นก่อนหน้าของ TensorFlow นอกจากนี้คุณยังสามารถสำรวจระบุแหล่งที่มาคุณสมบัติและผลกระทบของเส้นเขตแดนที่แตกต่างกันใน distill.pub

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