สํารวจศิลปะข้ามวัฒนธรรมและขนาดกลางด้วย Fast, Conditional, k-Nearest Neighbors
บทความนี้ทําหน้าที่เป็นแนวทางในการค้นหารายการที่ตรงกันผ่าน k-nearest-neighbors คุณสร้างโค้ดที่อนุญาตให้มีการคิวรีที่เกี่ยวข้องกับวัฒนธรรมและสื่อการเรียนการสอนศิลปะจากพิพิธภัณฑ์ศิลปะมหานครนครนิวยอร์กและ Rijksmuseum ในอัมสเตอร์ดัม
ข้อกำหนดเบื้องต้น
- แนบสมุดบันทึกของคุณเข้ากับเลคเฮ้าส์ ทางด้านซ้าย เลือก เพิ่ม เพื่อเพิ่มเลคเฮาส์ที่มีอยู่หรือสร้างเลคเฮ้าส์
ภาพรวมของ BallTree
โครงสร้างที่ทํางานอยู่เบื้องหลังแบบจําลอง KNN คือ BallTree ซึ่งเป็นต้นไม้ไบนารีแบบเรียกใช้ซ้ําซึ่งแต่ละโหนด (หรือ "ลูก") มีพาร์ติชันของจุดข้อมูลที่จะคิวรี การสร้าง BallTree เกี่ยวข้องกับการกําหนดจุดข้อมูลให้กับ "ลูก" ที่จุดกึ่งกลางที่พวกเขาใกล้เคียงที่สุด (ตามคุณลักษณะเฉพาะบางประการ) ส่งผลให้มีโครงสร้างที่ช่วยให้วิถีทางต้นไม้ไบนารีและ lends ตัวเองเพื่อค้นหาเพื่อนบ้าน k-ที่ใกล้ที่สุดที่ใบ BallTree
ตั้งค่า
นําเข้าไลบรารี Python ที่จําเป็นและเตรียมชุดข้อมูล
from synapse.ml.core.platform import *
if running_on_binder():
from IPython import get_ipython
from pyspark.sql.types import BooleanType
from pyspark.sql.types import *
from pyspark.ml.feature import Normalizer
from pyspark.sql.functions import lit, array, array_contains, udf, col, struct
from synapse.ml.nn import ConditionalKNN, ConditionalKNNModel
from PIL import Image
from io import BytesIO
import requests
import numpy as np
import matplotlib.pyplot as plt
from pyspark.sql import SparkSession
# Bootstrap Spark Session
spark = SparkSession.builder.getOrCreate()
ชุดข้อมูลของเรามาจากตารางที่ประกอบด้วยข้อมูลงานศิลปะจากทั้งพิพิธภัณฑ์ Met และ Rijks สคีมามีดังนี้:
- id: ตัวระบุที่ไม่ซ้ํากันสําหรับชิ้นส่วนของศิลปะ
- ตัวอย่าง Id ที่พบ: 388395
- ตัวอย่าง Rijks id: SK-A-2344
- หัวข้อ: ชื่อชิ้นศิลปะ ดังที่เขียนในฐานข้อมูลของพิพิธภัณฑ์
- ศิลปิน: ศิลปะชิ้นงานศิลปะ ที่เขียนในฐานข้อมูลของพิพิธภัณฑ์
- Thumbnail_Url: ตําแหน่งที่ตั้งของรูปขนาดย่อ JPEG ของชิ้นงานศิลปะ
- Image_Url ตําแหน่งที่ตั้งของภาพชิ้นงานศิลปะที่โฮสต์บนเว็บไซต์ Met/Rijks
- วัฒนธรรม: ประเภทของวัฒนธรรมที่ชิ้นศิลปะอยู่ใต้
- หมวดหมู่วัฒนธรรมตัวอย่าง: ละตินอเมริกา, อียิปต์ ฯลฯ
- การจําแนกประเภท: ประเภทของสื่อที่ชิ้นศิลปะอยู่ใต้
- ประเภทกลางตัวอย่าง: งานไม้, ภาพวาด, ฯลฯ
- Museum_Page: เชื่อมโยงไปยังงานศิลปะบนเว็บไซต์ Met/Rijks
- Norm_Features: การฝังของภาพชิ้นงานศิลปะ
- พิพิธภัณฑ์: ระบุพิพิธภัณฑ์ที่มีต้นกําเนิดมาจากพิพิธภัณฑ์
# loads the dataset and the two trained CKNN models for querying by medium and culture
df = spark.read.parquet(
"wasbs://publicwasb@mmlspark.blob.core.windows.net/met_and_rijks.parquet"
)
display(df.drop("Norm_Features"))
กําหนดประเภทที่จะสอบถาม
มีการใช้แบบจําลอง KNN สองแบบ: แบบหนึ่งแบบสําหรับวัฒนธรรมและอีกแบบหนึ่งคือแบบปานกลาง
# mediums = ['prints', 'drawings', 'ceramics', 'textiles', 'paintings', "musical instruments","glass", 'accessories', 'photographs', "metalwork",
# "sculptures", "weapons", "stone", "precious", "paper", "woodwork", "leatherwork", "uncategorized"]
mediums = ["paintings", "glass", "ceramics"]
# cultures = ['african (general)', 'american', 'ancient american', 'ancient asian', 'ancient european', 'ancient middle-eastern', 'asian (general)',
# 'austrian', 'belgian', 'british', 'chinese', 'czech', 'dutch', 'egyptian']#, 'european (general)', 'french', 'german', 'greek',
# 'iranian', 'italian', 'japanese', 'latin american', 'middle eastern', 'roman', 'russian', 'south asian', 'southeast asian',
# 'spanish', 'swiss', 'various']
cultures = ["japanese", "american", "african (general)"]
# Uncomment the above for more robust and large scale searches!
classes = cultures + mediums
medium_set = set(mediums)
culture_set = set(cultures)
selected_ids = {"AK-RBK-17525-2", "AK-MAK-1204", "AK-RAK-2015-2-9"}
small_df = df.where(
udf(
lambda medium, culture, id_val: (medium in medium_set)
or (culture in culture_set)
or (id_val in selected_ids),
BooleanType(),
)("Classification", "Culture", "id")
)
small_df.count()
กําหนดและพอดีกับแบบจําลอง ConditionalKNN
สร้างแบบจําลอง ConditionalKNN สําหรับทั้งคอลัมน์สื่อและวัฒนธรรม แต่ละแบบจําลองจะใช้ในคอลัมน์ผลลัพธ์ ฟีเจอร์คอลัมน์ (feature vector) คอลัมน์ค่า (ค่าเซลล์ภายใต้คอลัมน์ผลลัพธ์) และคอลัมน์ป้ายชื่อ (คุณภาพของ KNN ที่เกี่ยวข้องมีเงื่อนไขอยู่)
medium_cknn = (
ConditionalKNN()
.setOutputCol("Matches")
.setFeaturesCol("Norm_Features")
.setValuesCol("Thumbnail_Url")
.setLabelCol("Classification")
.fit(small_df)
)
culture_cknn = (
ConditionalKNN()
.setOutputCol("Matches")
.setFeaturesCol("Norm_Features")
.setValuesCol("Thumbnail_Url")
.setLabelCol("Culture")
.fit(small_df)
)
กําหนดวิธีการจับคู่และการแสดงภาพ
หลังจากตั้งค่าชุดข้อมูลเริ่มต้นและประเภทแล้ว ให้เตรียมวิธีการที่จะสอบถามและแสดงภาพผลลัพธ์ของ KNN แบบมีเงื่อนไข
addMatches()
สร้าง Dataframe ด้วยคู่ที่ตรงกันสําหรับแต่ละหมวดหมู่
def add_matches(classes, cknn, df):
results = df
for label in classes:
results = cknn.transform(
results.withColumn("conditioner", array(lit(label)))
).withColumnRenamed("Matches", "Matches_{}".format(label))
return results
plot_urls()
การโทร plot_img
เพื่อแสดงภาพการจับคู่ที่ด้านบนสําหรับแต่ละประเภทลงในเส้นตาราง
def plot_img(axis, url, title):
try:
response = requests.get(url)
img = Image.open(BytesIO(response.content)).convert("RGB")
axis.imshow(img, aspect="equal")
except:
pass
if title is not None:
axis.set_title(title, fontsize=4)
axis.axis("off")
def plot_urls(url_arr, titles, filename):
nx, ny = url_arr.shape
plt.figure(figsize=(nx * 5, ny * 5), dpi=1600)
fig, axes = plt.subplots(ny, nx)
# reshape required in the case of 1 image query
if len(axes.shape) == 1:
axes = axes.reshape(1, -1)
for i in range(nx):
for j in range(ny):
if j == 0:
plot_img(axes[j, i], url_arr[i, j], titles[i])
else:
plot_img(axes[j, i], url_arr[i, j], None)
plt.savefig(filename, dpi=1600) # saves the results as a PNG
display(plt.show())
รวมทุกอย่างเข้าด้วยกัน
กําหนด test_all()
ที่จะใช้ในข้อมูล แบบจําลอง CKNN ค่า id ศิลปะที่จะสอบถาม และเส้นทางของไฟล์เมื่อต้องบันทึกการแสดงภาพเอาต์พุต แบบจําลองขนาดกลางและวัฒนธรรมได้รับการฝึกและโหลดมาก่อนหน้านี้
# main method to test a particular dataset with two CKNN models and a set of art IDs, saving the result to filename.png
def test_all(data, cknn_medium, cknn_culture, test_ids, root):
is_nice_obj = udf(lambda obj: obj in test_ids, BooleanType())
test_df = data.where(is_nice_obj("id"))
results_df_medium = add_matches(mediums, cknn_medium, test_df)
results_df_culture = add_matches(cultures, cknn_culture, results_df_medium)
results = results_df_culture.collect()
original_urls = [row["Thumbnail_Url"] for row in results]
culture_urls = [
[row["Matches_{}".format(label)][0]["value"] for row in results]
for label in cultures
]
culture_url_arr = np.array([original_urls] + culture_urls)[:, :]
plot_urls(culture_url_arr, ["Original"] + cultures, root + "matches_by_culture.png")
medium_urls = [
[row["Matches_{}".format(label)][0]["value"] for row in results]
for label in mediums
]
medium_url_arr = np.array([original_urls] + medium_urls)[:, :]
plot_urls(medium_url_arr, ["Original"] + mediums, root + "matches_by_medium.png")
return results_df_culture
การสาธิต
เซลล์ต่อไปนี้ดําเนินการคิวรีแบบกลุ่มที่ระบุรหัสรูปภาพที่ต้องการและชื่อไฟล์เพื่อบันทึกการแสดงภาพ
# sample query
result_df = test_all(small_df, medium_cknn, culture_cknn, selected_ids, root=".")