米国における不動産査定審査のAI自動化PoC事例を知る
以下の内容は、AWS Communityの記事「Building an Appraiser Assistive Tool using Amazon Nova Pro」を日本語に翻訳したものです。適宜、補足を入れていますが、概ね原文通りです。
AIによる不動産査定支援ツール(AAT)で遅くて一貫性のない査定を効率化・標準化する
概要
不動産査定は不動産業界や住宅ローン業界にとって不可欠ですが、従来の査定プロセスは時間がかかり、担当者によるバラつきも課題です。
本記事では、AIを活用した不動産査定ツール(Appraiser Assistive Tool、AAT)が、画像認識と機械学習によって査定プロセスを効率化・標準化する仕組みを解説します。
特に、Fannie Maeの物件状態・品質評価システムと連携し、Amazon Nova Proを用いた概念実証(PoC)を紹介します。
Fannie Maeの評価基準
Fannie Maeの物件状態(Condition)と品質(Quality)は、ライセンスを持つ査定士による目視検査で決まります。
評価はC1〜C6(状態)、Q1〜Q6(品質)のスケールで、物理的な状態・メンテナンス・建築品質・材料などを総合的に判断します。
この標準化された評価は、貸し手がリスクを評価し、物件の適格性やローン条件を決める際の基準となります。
詳細はProperty Condition Ratingsをご参照ください。
AIによる自動化の意義
Fannie Maeの評価は目視検査が前提ですが、画像解析AIを活用することで、
- 欠陥の自動検出
- 材料品質や摩耗パターンの評価
- 建築特徴の判定
などを一貫性高く自動化できます。
AIは査定士のアシスト役として、効率と精度を向上させつつ、最終判断は人間が行うことでプロフェッショナルな監督・責任を維持できます。
AATの仕組みと出力例
AATは、システムプロンプトを使って物件画像を処理し、以下を自動生成します。
- Fannie Mae物件状態評価(C1〜C6)
- 対応する数値評価(1〜6、小数点2桁まで対応)
- 評価理由(観察ポイント)
AATは、Amazon Nova Proを使用し、Fannie Mae物件状態評価を生成します。Amazon Nova Proは、マルチモーダルな画像・テキスト処理に優れ、精度・速度・コストのバランスが良いモデルです。
AATへの入力
以下のサンプル不動産画像を、自動化したAAT測定の評価に使用します。画像は、Amazon Nova Canvasにより作成されたものです。
前面図
台所
寝室
浴室
裏庭
システムプロンプト例
あなたは経験豊富な不動産鑑定士です。提供された物件画像を分析し、以下の基準に基づいて詳細な評価を行ってください。
C1 - 新築/未入居
改修の必要なし 新品同様の状態 新築または最近リノベーションされた可能性あり
C2 - 保守遅延なし
最近の更新/リノベーション 最小限の摩耗や損傷 高品質な素材と施工
C3 - 良好に維持管理されている
軽微な物理的劣化のみ 通常の摩耗や損傷 いくつかの軽微な修理/更新が必要 依然として良好な状態
C4 - 軽微な保守遅延あり
いくつかの修理が必要 劣化が一部見られる 機能的だが経年や摩耗が見られる
C5 - 重大な保守遅延
大規模な修理が必要 複数の箇所で修理/交換が必要 著しい劣化 安全性や健全性に影響する可能性あり 大規模な改修が必要な場合あり
C6 - 深刻な保守遅延
大きな損傷/荒廃 健康・安全上の問題あり 大規模なリノベーションが必要 現状では居住不可
以下を提供してください:
1. C1からC6の間のレター評価
2. 1から6の間の数値評価(C1が1、C6が6に対応。中間の場合は小数点以下2桁まで記載)
3. 評価に影響した主な観察ポイント
回答は以下の構造のJSONオブジェクトとしてフォーマットしてください:
{
"letter_rating": <C1-C6>,
"numeric_rating": <1-6>,
"key_observations": ["<観察ポイント1>", "<観察ポイント2>", ...]
}
原文プロンプト
You are an experienced real estate appraiser. Analyze the provided property image and provide a detailed appraisal rating based on the following criteria:
C1 - Newly constructed/Never occupied
No improvements needed Like-new condition May be new construction or recently renovated
C2 - No deferred maintenance
Recent updates/renovations Minimal wear and tear High-quality materials and workmanship
C3 - Well-maintained
Limited minor physical deterioration Normal wear and tear Some minor repairs/updates needed Still in good condition
C4 - Some minor deferred maintenance
In need of some repairs Some deterioration visible Functional but showing age/wear
C5 - Major deferred maintenance
Needs significant repairs Multiple items needing repair/replacement Substantial deterioration May affect safety/soundness May need substantial rehabilitation
C6 - Severe deferred maintenance
Substantial damage/disrepair Health/safety issues present Major renovation required Uninhabitable in current condition
Please provide:
1. A letter rating between C1 and C6
2. A numeric rating between 1 and 6 and use 2 decimal places when the rating is in between with 1 at C1 and 6 at C6
3. Key observations that influenced your assessment
Format your response as a JSON object with the following structure:
{
"letter_rating": <C1-C6>,
"numeric_rating": <1-6>,
"key_observations": ["<observation1>", "<observation2>", ...]
}
AATの出力
出力
活用のポイント・注意事項
不動産査定ツール(AAT)のPoCにより、人間の監督を介入させながらも、不動産査定審査をAIで強化できることが実演できました。評価の一貫性を保ちつつ、専門的な不動産査定の知見を入れた機械学習モデルの速度をミックスすることで、以下を期待することができます。
- 地域差のない一貫した評価が可能
- 自動観察ログ(automated observation logging)でドキュメント化が容易
- 標準化された評価基準で精度向上
(注)
Amazon Bedrock Guardrails(公式ドキュメント)を活用し、責任あるAI運用・セーフガードを実装してください
謝辞
本記事の開発にはMallik Panchumarthy氏の技術的貢献が大きく、彼の提案や問題解決アプローチがプロジェクト成功の鍵となりました。心より感謝いたします。
付録
シーケンス図
Pythonモジュール
この機能の実行時、Amazon Nova Proにアクセスがあり、推論プロファイルを使用していることを確認してください。システムプロンプトは、ご自身のニーズに沿って変更してください。画像フォーマットはjpg
を想定しており、他のフォーマットを使用する場合は改修が必要です。
from typing import List
# Import specific Boto3 client for AWS Bedrock
from boto3 import client as boto3_client
import time
from botocore.exceptions import ClientError
import random
def analyze_property_images_with_bedrock(
image_paths: List[str],
inference_profile_arn: str,
system_prompt: str
) -> str:
"""
Analyze frames using Amazon Bedrock Converse API.
Args:
images: List of paths to property images
inference_profile_arn: ARN of the Bedrock inference profile
system_prompt: use prompt text from `property_condition_rating.txt` for property condition rating
or use prompt text from `quality_of_construction_rating.txt` for quality rating
Returns:
Letter rating, numeric rating, and the reason for the generated rating
"""
bedrock_runtime = boto3_client(
service_name="bedrock-runtime",
# Extract region from ARN
region_name=inference_profile_arn.split(":")[3]
)
all_ratings = []
# Prepare the messages for conversation
for image_path in image_paths:
messages = []
# Read image as bytes without encoding
with open(image_path, "rb") as image_file:
image_bytes = image_file.read()
messages.append({
"role": "user",
"content": [{
"image": {
"format": "jpeg",
"source": {
# Send raw bytes directly
"bytes": image_bytes
}
}
}]
})
# Use the Converse API with retry logic
response = call_converse_with_retry(
bedrock_runtime,
modelId=inference_profile_arn,
messages=messages,
system=[{
"text": system_prompt
}]
)
# Extract content from response
if ('output' in response and
'message' in response['output'] and
'content' in response['output']['message']):
rating = response['output']['message']['content'][0]['text']
# print(f"{image_path} rating is: {rating}")
all_ratings.append(rating)
else:
print(f"Unexpected response format: {response}")
# Generate final summary using Converse API
try:
# Combine all ratings into a single composite rating
combined_ratings = "\n\n".join(all_ratings)
print("Starting final rating...")
final_response = call_converse_with_retry(
bedrock_runtime,
modelId=inference_profile_arn,
messages=[{
"role": "user",
"content": [{
"text": combined_ratings
}]
}],
system=[{
"text": (
"Provide a overall numeric rating, letter rating, and the reason for the rating. "
"Letter rating C1 is the best while C6 is the worst."
"Numeric rating 1 is the best while 6 is the worst."
"The overall rating should be based on the worst case rating."
)
}]
)
# Extract content from final response
if ('output' in final_response and
'message' in final_response['output'] and
'content' in final_response['output']['message']):
return final_response['output']['message']['content'][0]['text']
else:
print(f"Unexpected final response format: {final_response}")
return "Error: Unexpected response format"
except Exception as e:
print(f"Error generating final summary: {str(e)}")
return "Error generating summary"
def call_converse_with_retry(bedrock_runtime, **kwargs):
"""
Call the Bedrock Converse API with exponential backoff retry logic.
Args:
bedrock_runtime: Bedrock runtime client
**kwargs: Arguments to pass to the converse API
Returns:
API response
"""
max_retries = 5
base_delay = 1 # Starting delay of 1 second
for attempt in range(max_retries):
try:
return bedrock_runtime.converse(**kwargs)
except ClientError as e:
error_code = e.response['Error']['Code']
if error_code == 'ThrottlingException':
if attempt == max_retries - 1: # Last attempt
raise # Re-raise the exception if we've exhausted all retries
# Calculate exponential backoff with jitter
delay = (2 ** attempt * base_delay) + random.random()
print(f"ThrottlingException encountered. Retrying in {delay:.2f} seconds...")
time.sleep(delay)
else:
raise # Re-raise if it's not a throttling error
except Exception as e:
# For other exceptions, don't retry
raise
本記事の内容は筆者個人の見解であり、AWS公式見解を示すものではありません。
Discussion