見出し画像

[BIM×生成AI] IFCファイルを生成AIで扱うためのグラフデータベース活用の検証と考察

この記事について


建設業界で活用がしている3D設計図BIMを生成AIを用いて解析、生成AIに自然言語で質問し回答を得ることを目指します。
例:
「部屋は何部屋ありますか?」
「1Fに8部屋、2Fに1部屋です」
これを実現することで、設計上の不整合、法令遵守チェックが自然言語で出来るようになり、新たなチェック項目が増えても誰でも素早く確認ができます。

技術的にボトルネックとなるのはBIM情報の解析部分であり、数十MBとファイルサイズが大きいBIMの情報を生成AIはそのまま解析できず、出来ても不正確な情報を返してしまいます。
この記事ではBIMのファイル(IFCファイル)をグラフデータベースに変換、グラフ構造で情報を保持することで、生成AIがBIMの情報に正しくアクセスできる状態を目指します。

BIMファイルの取り扱いにクラウドを中心としたソフトウェア技術を持ち込むことで、BIMの更なる利用発展が進むことを念頭においています。


BIMの可能性と、生成AIによるさらなる可能性


BIMとは

BIM(Building Information Modeling)は、建物の情報を3Dモデルで管理する仕組みです。単にビルの構造を3Dで表現しているだけではなく、部材の材質やコストといったデータも含まれています。

  • 配管やダクトが柱や梁と干渉していないかを3Dモデル上で確認できる

  • 構造設計者と設備担当者が設計の不整合や認識のズレを防げる

  • 3Dモデルを基に部材の配置や寸法を直感的に理解できる

などができ、多数の関係者が協働する建設の工程において業務効率の改善が大きく期待されます。

buildingSMART Internationalという団体がBIMを国際標準として整えており、IFC(Industry Foundation Classes)という形式を使えば、どんなプロジェクトでもデータを共有できるようになっています。

業界として標準化が進んでいるからこそ、活用の可能性が広がっています。

生成AI登場によるBIM活用の可能性

BIMの構造化されたデータに対して、設計の不整合チェックや法律遵守チェックを
「2階の天井にあるダクトが梁と干渉していないか確認して」
「非常階段の幅は建築基準法に適合していますか?」
「窓ガラスの仕様は耐風圧性能の基準を満たしていますか?」

など、曖昧さを含む表現で確認することが出来るようになる可能性があります。
法律などは自然言語で書かれているルールであり、ルールを解釈しチェックツールに正確に落とし込むのは運用が大変でした。
チェックを自然言語で行えるようになることで、設計チェックや法律尊種チェックでの活用が加速する可能性を秘めています。

今回の検証で使うIFCファイルについて


GitHubで公開されているこちらのIFCファイルを検証に利用します。

・1F建ての建物が1つ、2F建ての建物が1つ
・2つの建物を合わせて、1Fに部屋が8つ
・2つの建物を合わせて、1Fに机が14個
が含まれている、シンプルな構造の建物です。

IFC ビュワーで開いた例
1Fの平面図
配置されている机

ファイルの大きさ等は下記の通りです。
・ファイルサイズ:1.23MB
・行数: 20765行
・文字数: 1271830文字

# echo "Chars  Lines  Size(MB)  File" && awk '{l++; c+=length} END {printf "%s  %s  %.2f  %s\n", c, l, size/1024/1024, FILENAME}' size=$(stat -c %s /app/data/LargeBuilding.ifc) /app/data/LargeBuilding.ifc
Chars  Lines  Size(MB)  File
1271830  20765  1.23  

残念ながら、IFCファイルそのままではLLMは処理してくれない


IFCファイル添付して解析しても、精度は出ない

ChatGPT等のLLM等はファイル添付機能をサポートしており、一見
・IFCファイルを添付する
・IFCファイルの構造について質問する

だけで、IFCファイルの内容について正しい回答が得られそうに思えます。

しかし実際にやってみると、解析を断られるか、質問のたびに違う内容が返ってきます。

あなたはIFCのプロです。
何階建てで、何部屋あるか教えて。

ChatGPTへの質問内容

IFCファイルを解析した結果、以下の通りです。
階数:2階 部屋数:16部屋

ChatGPT 回答1回目

ファイルを解析した結果、以下の情報が得られました。
階数:5階建て 部屋数:42部屋

ChatGPT 回答2回目

LLMは、膨大な文字列情報から正しく情報を返すのが苦手

今回検証に利用したシンプルなIFCファイルにおいても
・ファイルサイズ:1.4MB
・行数: 20765行
・文字数: 1271830文字
のサイズであり、この中に建物の要素情報と要素の関係値、座標情報等が含まれています。

LLMは、大量のテキストを一度に処理する際にトークン制限コンテキストの圧縮が発生し、情報の欠落や歪みが生じやすいという特性があります。特に、長文の中から特定の情報を正確に抽出するのは苦手であり、関連性の低い情報まで考慮してしまうことで回答の精度が低下します。

この解決策として、バックオフィス向けのチャットボットなどではRAG(Retrieval-Augmented Generation)が使われます。質問に対してコサイン類似度を用いて関連するドキュメントを検索し、その内容をLLMに渡して回答を生成します。
しかし、BIMデータは建物の要素が階層構造を持つため、単純に「似ている文書を探す」だけでは要素間の関係性を正確に抽出出来ないという課題があります。

IFCファイルの正確な解釈には、グラフ構造でのデータ保持が必要

LLMに正しく解釈させるには、「どの階にあるか」「どの部屋に属するか」といった関係性情報をグラフ構造として保持し、要素間のつながりを反映することが重要です。
これにより、「1階の部屋数を教えて」「2階の窓のサイズは?」といった質問に対して、要素同士のリレーションをたどり、必要な情報を正確に取得できます。

IFCファイルの構造について


IFCファイルの実態は、要素と要素間の関係を記した大きなテキストファイルである

今回の検証に入る前に、まずはIFCファイルの構成を簡単に説明します。
下記がIFCファイルの中の疑似コードです。

#1 = IFCPROJECT('P1', 'Office_Building');  // プロジェクト全体  
#2 = IFCSITE('S1', 'Main_Site', #1);        // 敷地 (#1 プロジェクトに属する)  
#3 = IFCBUILDING('B1', 'Building_A', #2);   // 建物 (#2 敷地に属する)  
#4 = IFCBUILDINGSTOREY('F1', '1F', #3);     // 1階 (#3 建物に属する)  
#5 = IFCBUILDINGSTOREY('F2', '2F', #3);     // 2階 (#3 建物に属する)  
#6 = IFCSPACE('Room101', '1F_Room1', #4);   // 1階の部屋 (#4 1階に属する)  
#7 = IFCSPACE('Room102', '1F_Room2', #4);   // 1階の別の部屋 (#4 1階に属する)  
#8 = IFCSPACE('Room201', '2F_Room1', #5);   // 2階の部屋 (#5 2階に属する)  

大きく、
・要素の定義(プロジェクト、建物、階、壁など)
・関係性の定義
の2つに分かれています。

要素の定義

要素としては下記などがあり、各行において明示的に要素が宣言されています。

#1 = IFCPROJECT('P1', 'Office_Building');  // プロジェクト全体  
#2 = IFCSITE('S1', 'Main_Site', #1);        // 敷地 (#1 プロジェクトに属する)  
#3 = IFCBUILDING('B1', 'Building_A', #2);   // 建物 (#2 敷地に属する)  
#4 = IFCBUILDINGSTOREY('F1', '1F', #3);     // 1階 (#3 建物に属する)   
  • IfcProject:プロジェクト全体

  • IfcSite:敷地

  • IfcBuilding:建物

  • IfcBuildingStorey:階層(1階、2階)

  • IfcSpace:部屋や空間

  • IfcWall:壁

  • IfcWindow:窓

関係性の定義

各要素は建物の基本構造として下記の関係性を持っています。

IfcProject  
 └ IfcSite  
    └ IfcBuilding  
       └ IfcBuildingStorey(階層)  
          └ IfcSpace(部屋)  
          └ IfcWall(壁)  
             └ IfcWindow(窓)  

具体的な関係性は下記のようにIFCSPACEの要素で定義されています。

#6 = IFCSPACE('Room101', '1F_Room1', #4);   // 1階の部屋 (#4 1階に属する)  
#7 = IFCSPACE('Room102', '1F_Room2', #4);   // 1階の別の部屋 (#4 1階に属する)  
#8 = IFCSPACE('Room201', '2F_Room1', #5);   // 2階の部屋 (#5 2階に属する)

IFCファイルはグラフ構造として表現できる

IFCファイルは、建物の各要素をノード(Node)、要素間の関係性をエッジ(Edge)として捉えることで、グラフ構造として表現できます。

例えば、「1階に壁がある」「壁に窓が取り付けられている」といった情報は、壁や窓がノード、それらをつなぐ「属している」「取り付けられている」といった関係がエッジになります。

このようにIFCファイルの階層的なデータは、ノードとエッジの組み合わせで直感的にモデル化でき、LLMが処理しやすい形に変換する土台となります。として捉えることで、グラフ構造として表現できます。

グラフデータベースを用いたLLMでの質問回答の概要

IFCファイルの登録からLLMによる回答生成までのワークフロー

今回の「BIMを生成AIを用いて解析、生成AIに自然言語で質問し回答を得る」を実現するために、
建物の情報をグラフデータベースの形式で保存しておき、LLMの実行時にグラフデータベースから必要な情報だけ取得して回答する
方針をとります。
グラフデータベースにはneo4jを利用します。

具体的には、下記処理を行います。

  • [事前準備]IFCファイルをクエリに変換し、建物要素と関係性をグラフデータベースに登録・保存

  • [回答時]LLMが自然言語の質問をクエリに変換し、グラフデータベースを検索を実行。取得した結果をもとにLLMが回答を構築


IFCファイルをアップロードしてLLMで質問をするまでのワークフロー

グラフデータベースを用いたLLMでの質問回答の技術的検証

全体の処理フロー

上記のワークフローを実行するにあたり、技術的には下記を想定しています。

IFCファイルの登録(前準備)

  • 1.IFCファイルの解析
    ifcopenshellでIFCファイルを解析し、IfcBuilding, IfcStorey, IfcSpaceなどの建物要素を取得

  • 2.要素を登録するCypherクエリの生成
    各要素(階層、部屋など)をNeo4jにノードとして登録するCypherクエリを生成。
    例:CREATE (s:IfcSpace {name: "Room101"})

  • 3.要素間の関係を登録するCypherクエリの生成
    「1階に部屋がある」といった要素間の関係性をNeo4jに登録するCypherクエリを生成。
    例:MATCH (f:IfcStorey {name: "1F"}) MERGE (f)-[:HAS_SPACE]->(s)

  • 4.Cypherクエリの実行とデータ登録
    Pythonのneo4jドライバを使い、生成したCypherクエリを実行して建物要素と関係性をNeo4jに登録

 ユーザーの質問に対するクエリ生成と回答構築

  • 5.自然言語の質問から検索クエリを生成
    LLMが「1階の部屋を教えて」という質問を該当するノードを検索するCypherクエリに変換。
    例:MATCH (s:IfcStorey)-[:HAS_SPACE]->(r:IfcSpace) WHERE s.name="1F" RETURN r

  • 6.検索クエリの実行とデータ取得
    生成したCypherクエリをNeo4jに実行し、該当するIfcSpaceノードを取得。

  • 7.LLMによる回答生成
    取得したデータを元に、LLMが「1階には8部屋あります」といった自然言語の回答を構築。

1.IFCファイルの解析

IFCファイルの解析には、ifcopenshellというPythonライブラリを使用しました。
ライブラリについての詳細な説明は省きますが、IFCファイルを読み込ませれば比較的容易に要素および関係性の抽出が可能です。

ファイル読み込み、ノードの取得処理例


        model = ifcopenshell.open(file_path)
        node_queries = []

        # ノード作成
        for entity in model:
            global_id = getattr(entity, "GlobalId", None)

            node_type = entity.is_a()
            properties = {
                "GlobalId": global_id,
                "Name": getattr(entity, "Name", None),
                "Description": getattr(entity, "Description", None),
                "PredefinedType": getattr(entity, "PredefinedType", None),
            }

            properties_str = ", ".join([f"{key}: '{value}'" for key, value in properties.items() if value])
            node_queries.append(f"CREATE (n:{node_type} {{{properties_str}}});"

2.要素を登録するCypherクエリの生成

IFCファイルから生成したnodeをNeo4jに登録するには、CypherのCREATE文を生成する必要があります。
取得した要素をGlobalIdを主キーとして下記のようなCREATE文に整形します。

CREATE (n:IfcBuilding {GlobalId: '2nxdYR2RHCDBiKJulbA_QV', Name: '// BUILDING/NAME //'});

3.要素間の関係を登録するCypherクエリの生成

要素と同様に関係性の登録も別途クエリを生成します。MATCH文を下記のようにGlobalId同士の関係性で定義します。

MATCH (parent {GlobalId: '2cXV28XOjE6f6irgi0COSw'}), (child {GlobalId: '2cXV28XOjE6f6irga0COSw'}) CREATE (parent)-[:DECOMPOSES]->(child);

なお、IFCにおける要素間の関係性は大きく下記5種類があるため、それぞれ抽出、MATCH文を生成します。

  • 分解関係:IfcRelDecomposes(建物→階→部屋)

    • 例:(parent)-[:DECOMPOSES]->(child)

  • 包含関係:IfcRelContainedInSpatialStructure(階に壁を含む)

    • 例:(structure)-[:CONTAINS]->(element)

  • 接続関係:IfcRelConnects(壁に窓が接続)

    • 例:(relating)-[:CONNECTS_TO]->(related)

  • 関連関係:IfcRelAssociates(部屋とプロジェクトを関連付け)

    • 例:(element)-[:ASSOCIATED_WITH]->(:Association)

  • 材料関係:IfcRelAssociatesMaterial(壁にコンクリートを設定)

    • 例:(element)-[:HAS_MATERIAL]->(:Material)

4.Cypherクエリの実行とデータ登録

Pythonのスクリプト生成したCREATE文、MATCH文(合わせて3846件)をneo4jのデータベースに対して実行し登録します。
登録した結果が下記です。

全要素を取得するクエリを実行すると、下記のようなnodeとedgeの相関図を出すことができます。

MATCH (n)-[r]->(m)
RETURN n, r, m  
全てのノードの関係図。下の集合体が1F、上の集合体が2Fの要素になっている

フロア、部屋情報に絞ってみると、このIFCファイルは
1Fに8部屋がある(2つの建物の区別はしない)
構成になっていることが分かります。

MATCH (building:IfcBuilding)-[r]->(storey:IfcBuildingStorey)
OPTIONAL MATCH (storey)-[r2]->(space:IfcSpace)
RETURN building, r, storey, r2, space
フロア、部屋情報に絞った出力
IFCビュワーでみた1Fの構成

5.自然言語の質問から検索クエリを生成

「何階建てで、何部屋あるか知りたい」と聞かれた時に、まずはLLMを用いて必要な情報を取得するCypherクエリを発行します。
今回作成したプロンプトの一部を紹介します。

概要: Neo4jのCypherクエリを生成するための特化ツールとして、自然言語での質問を直接的かつ正確に、実行可能なCypherクエリへ変換します。Neo4jのデータスキーマとデータベースの構造を理解し、必要なラベルやプロパティ、リレーションシップを活用して、明確な応答を生成します。

指示: 厳格な応答形式:
応答は必ずNeo4jで実行可能なCypherクエリ形式で行います。クエリ以外の説明、コンテキスト、追加情報は一切含めません。
もしCypherクエリ以外の情報を求められた場合は、「その内容についてはCypherクエリでは対応できません」とクエリ内で明示します。

入力例

何階建てで何部屋あるか知りたい

出力例

floorCount:2
roomCount:8 

7.LLMによる回答生成

クエリ結果を元に、回答文を生成します。

出力

この建物は 2階建てで、8部屋が存在します。

今回の検証IFCファイルにおける精度について

何階建てで何部屋あるか知りたい

正解: この建物は 2階建てで、8部屋が存在します。
IfcBuildingStorey、IfcSpaceの関係性を正しく理解し抽出できている。

クエリ結果

部屋ごとに家具の数を教えて

(プロンプトを一部修正して)正解
※机という形で登録されていないため、家具として指示

最終的に正解にはなりましたが、当初IfcSpace(部屋)とIfcFurnishingElement(家具)の関係をHAS_FURNISHING_ELEMENTで検索しに行って取得できていませんでした。実際のファイルではCONTAINSで定義されていました。
関係性の定義はファイルによって異なる可能性があるため、LLM上でのクエリ生成においては関係性を指定せずにクエリを生成するよう指示した方が良さそうです。


各部屋ごとの家具の数は以下の通りです:

  • 部屋 8 – 1個

  • 部屋 7 – 3個

  • 部屋 6 – 2個

  • 部屋 5 – 2個

  • 部屋 2 – 3個

  • 部屋 1 – 3個

MATCH (space:IfcSpace)-[r]->(furniture:IfcFurnishingElement)
RETURN space.name AS roomName, space.globalId AS roomId, count(furniture) AS furnitureCount

使われている素材の種類数を教えて

正解: 13種類です。

MATCH (material:IfcMaterial)
RETURN count(DISTINCT material) AS materialCount

aluminumが使われている箇所を教えて

不正解: (取得できず)
IfcMaterialから逆算して探そうとしたが、neo4jへの登録の時に関係性が漏れていたからか取得できませんでした。
今回は簡易的に変換したため各属性において一部取得できていない箇所があると考えられます。

結論

IFCファイルの全要素をグラフデータベースに登録することで、LLMを用いて情報抽出が可能になる

・IFCファイルを適切にノードとエッジの情報に抽出、グラフデータベースに登録
・LLMはCypherクエリを作成・実行することで、必要な情報のみに沿って回答を生成する
を行えば、大量の文字列の処理が苦手なLLMにおいても、大規模なテキストファイルであるIFCファイルから正確な情報抽出が出来る可能性が検証できました。

前提として、
・IFCファイルが設計者によらず適切な情報粒度、関係性で作成されていること
・LLMがIFCファイルの構成通りのCypherクエリを作成すること
などがあるため、より複雑な利用においては精度検証およびチューニングが必要になる可能性があります。

LLM上でのCypherクエリ生成にはチューニングの余地あり

いくらグラフデータベース上でIFCファイルの建物情報を表現できたとしても、LLMがクエリ生成ミスをすれば、取得できず異なる結果が出てしまいます。
・IFCファイルの構造の所属組織ごとの傾向の確認
・LLMが誤って書いてしまいやすい関係性に関するクエリの調整
などを行い、環境に適したクエリが書けるように調整していく必要があります。

IFCファイルに含まれるノード、エッジ数は非常に多い。大規模なIFCファイルでの実施においては、グラフデータベースの構築性能を考慮する必要がある

今回は比較的要素が少ないIFCファイルで検証したが、ビジュアライズ用の要素も含めると21000件以上のnode・edgeが存在しました。
今回は検証のために適宜属性をブラックリストで弾くことで処理対象を削減したものの、約8000件の登録を行いました。
大規模なビルにおいてはノード数、エッジ数が非常に多くなることが想定されます。
より軽量かつ高速にグラフデータベースを構築できるようパフォーマンスチューニングをするか、IFCファイルへの直接クエリ実行するアプローチの検討が必要と考えられます。

いいなと思ったら応援しよう!