查找

Announcement
· Sep 4

August Article Bounty: Community Spotlight

August was an inspiring month for our Community 🙌

A huge thank you to all who joined the August Article Bounty 💙

Your contributions keep our knowledge base growing and make it easier for others to learn, solve problems, and get inspired.

Special shout-out to those who took the time to write brand-new articles and share their expertise (each received 5,000 points 🎉):

And also to those who joined the challenge and helped us in the search for interesting articles (each received 30 points 🙌):

👏 Thank you for your effort, creativity, and willingness to share knowledge.

And the good news is… the new Article Bounty is already waiting for you on Global Masters! Don’t miss the chance to take part and earn rewards this month.

Discussion (0)1
Log in or sign up to continue
Question
· Sep 4

How can I optimize the performance of my queries and cubes in Adaptive Analytics?

Hi Everyone, I'm Prakash Hinduja, a financial advisor and consultant. My roots in India now living in Geneva, Switzerland (Swiss). I'm looking for some suggestions on how to get the best performance from Adaptive Analytics 2023.2. I know it uses Adaptive Parallel Execution and automatic aggregates, but I'm curious if you've found any other tips or tricks. For example, are there any specific data modeling choices or system configurations that have worked well for you? I'm trying to make our cubes as responsive as possible. Thanks in advance for any insights!

Regards

Prakash Hinduja from Geneva, Switzerland (Swiss) 

Discussion (0)1
Log in or sign up to continue
Article
· Sep 4 9m read

ベクトルであそぼう! - マルチモーダルAIモデルとモダリティギャップ

開発者の皆様こんにちは。先日のWebinar「ベクトルであそぼう!」では、以下の内容でデータをベクトル化することの可能性をご紹介しました。

写真から魚の名前をあててみる

  • マルチモーダルモデル CLIP を利用して画像によるテキストの検索

ベクトルを「見える化」する

  • ベクトルを次元削減して 3 次元ベクトルに変換し、可視化

データの集まりを見る

  • K-Means によるデータのクラスタリング

変なデータ (=アノマリ) を見つける

  • K-Means による教師なしアノマリ検知や半教師ありアノマリ検知


一番お伝えしたかったのは 「データをベクトルに変換することで、データ利活用の幅が大きく広がる」 ということです。
本記事ではマルチモーダルAIおよびCLIPについておさらいし、Webinarでは時間の都合で触れきれなかったTips  - モダリティギャップというマルチモーダルモデル特有の現象についてお伝えします。

なお筆者は AI/機械学習の専門家ではありませんが、機械学習を利用したプロダクト・プロジェクトに携わり親しんでまいりました。ご質問・ご指摘などありましたらお気軽にコメント欄からお願いします。


マルチモーダル AI

近年、AI 分野では マルチモーダル AI が大きな注目を集めています。
「モーダル」とはデータの種類のことを指します。

  • 文章や会話などのテキスト
  • 写真やイラストなどの画像
  • 音声や BGM などの音
  • 動画やセンサーデータ

従来は、AI モデルは 単一モーダル(例:画像分類モデルは画像だけ、翻訳モデルはテキストだけ)を対象にしてきました。
一方、マルチモーダル AI は 異なるモーダルを同時に理解・処理できる のが特徴です。

例えば:

  • 画像を入力して説明文を生成する(画像 → テキスト)
  • 音声を文字に起こす(音声 → テキスト)
  • テキストで検索して関連する画像を見つける(テキスト → 画像)

このように複数のモーダルをまたいで処理できる能力は、検索・レコメンド・対話 AI・医療解析など幅広い応用につながります。
また、近年では生成AI(ChatGPT や画像生成モデルなど)もマルチモーダル化が進んでおり、テキストだけでなく画像や音声を統合的に扱えるようになってきました。その背景には、大規模言語モデル(LLM) が持つ強力なテキスト理解・知識表現の能力があり、マルチモーダルAIの発展を支える基盤技術となっています。


マルチモーダルAIモデル  - CLIP

ウェビナー『IRISのベクトル検索を使ってテキストから画像を検索してみよう』では、マルチモーダル AI モデル CLIP を利用して画像をテキストで検索できること、
ウェビナー『ベクトルであそぼう!』では、同じく CLIP を利用し、逆にテキストを画像で検索することにより、魚の画像から魚の名前をあてるデモンストレーションをしました。 IRIS にベクトルを格納することで、大規模なデータセットでも高速にベクトル検索が可能となる実験構成で行いました。



あらためてCLIPについておさらいしますと、
CLIP(Contrastive Language–Image Pretraining) は OpenAI が 2021 年に発表した画像と自然言語のマルチモーダルモデルです。
画像とテキストのペアを学習することで、画像からテキストを予測したり、画像をテキストで検索するといったことが可能となっています。

CLIPの学習には、インターネットから収集した約 4億組の画像+テキストペア が利用されました。

学習には入力データをベクトルに変換するモデル(エンコーダ)が必要となりますが、CLIPには画像とテキストでそれぞれ異なるエンコーダが利用されます。
画像のエンコーダには、CNNやViTといった画像処理のモデルが利用され、テキストのエンコーダにはTransformerが利用されています。TransformerはChatGPTのベースになっているので最近よく耳にする、という方も多いのではないでしょうか。

学習方法として「画像とそのキャプションをペアで入力」し、対応するペアの埋め込みを近づけ、他のペアは遠ざけるというコントラスト学習 (Contrastive Learning)が行われました。これにより、画像とテキストの類似度が判断できるようになるというわけです。


[出典:Radford et al., Learning Transferable Visual Models From Natural Language Supervision, ICML 2021]


ゼロショット性能

ゼロショット分類とは、機械学習モデルが訓練時に一度も見たことのないクラス(カテゴリ)を分類する能力のことです。
この技術は、事前のラベル付けされたデータがなくても、意味的な知識(例えば、視覚的な特徴とラベルの関係)や、事前学習済みモデルの持つ能力を活用して、未知のデータを予測します。
マルチモーダルモデルは、従来の単一モーダルのモデルよりもゼロショット分類の性能が高くなっています。画像だけで特徴を得るより、「テキスト」という汎用的な知識表現を組み合わせていることで性能が高くなっているのは興味深いですね。

転移学習性能

「転移学習(Transfer Learning)」とは、あるタスクで学習した知識を別のタスクに再利用する手法です。ゼロからすべてのモデルを学習させるのではなく、大規模データで事前に学習済みのモデルを出発点として活用し、少量のデータだけ追加学習(ファインチューニング)して目的のタスクに適応させます。
CLIP はゼロショットだけでなく 転移学習にも優れています。4億組の画像+テキストペアを学習しているため、画像の種類や文脈が非常に幅広いのが特徴です。そのため CLIP の埋め込み(ベクトル表現)を特徴量として利用し、少量のデータでファインチューニングするだけで、従来の 事前学習モデルよりも高い性能を発揮するケースが多く報告されています。
こうした性能により、医療、製造、EC、セキュリティなど専門的な分野でも応用が期待されます。


ベクトルの可視化とモダリティギャップ

ウェビナーでは、約100種の魚名テキストをCLIPによりベクトル化し、UMAPで次元削減して3次元空間に描画し可視化しました。


クロマグロ、ブリ、カツオといった大きめの回遊魚が近くに配置されたり、ヒラメ、カレイといった似ている魚も近くにいます。淡水魚が集まっていたり、エビ、イカタコはそれぞれ近くに配置されクラスターのようになっています。このように、可視化をすることでモデルがデータをどう解釈しているかを把握することができます。

ウェビナーではここまででしたが、同様に画像の特徴ベクトルも可視化していきます。こちらは、先程の魚名テキストと同様、20種ほどの魚の画像をCLIPによりベクトルを取得し、UMAPにより次元削減をし、3次元空間に描画したものです。画像ベクトルを可視化した場合も、テキストと同様に、類似しているものは近く、そうでないものは遠く配置されています。
(画像の背景等の影響を受けるため、直感的な想定とは若干異なった配置となっている可能性はあります)


では、CLIPが画像とテキストのベクトルを同時に扱えることから、画像の特徴ベクトルとテキストの特徴ベクトルを同じ空間に可視化するとどうなるでしょうか。
例えば、サンマの画像はサンマのテキスト、ボタンエビの画像はボタンエビのテキストに近いところに配置されるでしょうか?
結果はこのようになりました。


左の方に魚名テキストのベクトルが集まり、右の方に魚の画像ベクトルが集まっているようにみえます。
サンマのテキストははサンマの画像に近くに配置されるのでは?と予想したのですが、その通りになりませんね。
これは、モダリティギャップといわれ、マルチモーダルを扱う際に汎用的にみられる現象です。
同じモーダル同士 - 画像と画像、テキストとテキストの類似度は高く出やすく、異なるモーダル間 - 画像とテキストでは、同じ意味を持っていても数値的な類似度が低めに出ます。これは、画像とテキストの特徴表現がもともと性質の異なる情報であることに起因します。
画像はピクセルパターンから抽出された視覚的特徴であり、テキストは単語や文脈から抽出された意味的特徴となります。
両者を同じ空間に揃えるよう学習しても、完全には一致せず「ズレ」が残ってしまいます。これがモダリティギャップです。
このギャップを補正するような研究がされていて、今後さらに高精度なマルチモーダルモデルが期待されます。

[出典:Zhao et al., Bridging the Modality Gap in Multimodal Contrastive Learning, NeurIPS 2022.]


マルチモーダルAIとデータプラットフォーム

マルチモーダルAIの活用にはモデルだけでなく、データをどう蓄積・検索・活用するかという基盤が不可欠です。
例えば、CLIPのようなモデルは入力をベクトル化して処理しますが、実際にビジネスで利用する場合には:

  • 大規模なベクトルデータを効率的に保存できること
  • 高速なベクトル検索や類似度計算が可能であること
  • 他システムやアプリケーションと柔軟に連携できること

といった条件が求められます。

この観点から、IRIS のようなデータプラットフォームを利用することで:

  • テーブルにベクトルを格納し、高速な近似近傍探索(ANN検索)を行える
  • 相互運用性により様々なデータソースを統合しパイプラインを構築できる

といった利点があります。つまり、マルチモーダルAIモデルの可能性を現実のプロジェクトに落とし込むには、高性能かつ堅牢なデータプラットフォームが必要不可欠です。

といったところで本記事を締めたいと思います。


ご案内

ベクトルであそぼう!は 3回連続シリーズのAI関連セミナーの第2回であり、次回(最終回)のテーマは『RAG+生成AIであそぼう!』 (9月9日開催)です。ぜひご登録ください!


参考文献

関連リンク

Discussion (0)1
Log in or sign up to continue
Discussion (0)1
Log in or sign up to continue
Article
· Sep 3 5m read

How to Build a Usable Method With 50 Parameters or Leveraging JSON As Method Qualifiers

Hi folks!

Sometimes, when designing a class method and feeding it with more and more useful features, very soon the number of parameters can reach 10 and even more.

It becomes pretty difficult for users of useful methods to remember the position of the important parameter, and it is very easy to misuse the position and transfer the wrong value to the wrong parameter.

Here is an example of such a method (I asked GPT to create a method with 20 params):

ClassMethod GenerateReportWith20Params(
    pTitle As %String = "",
    pAuthor As %String = "",
    pDate As %String = "",            // e.g. 2025-09-03
    pCompany As %String = "",
    pDepartment As %String = "",
    pVersion As %String = "1.0",
    pFormat As %String = "pdf",       // pdf|html|docx
    pIncludeCharts As %Boolean = 1,
    pIncludeSummary As %Boolean = 1,
    pIncludeAppendix As %Boolean = 0,
    pConfidentiality As %String = "Public",
    pLanguage As %String = "en",
    pReviewers As %String = "",       // CSV, e.g. "Alice,Bob"
    pApprover As %String = "",
    pLogoPath As %String = "",
    pWatermarkText As %String = "",
    pColorScheme As %String = "default",
    pPageSize As %String = "A4",
    pOrientation As %String = "Portrait",
    pOutputPath As %String = "report.pdf"
) As %Status
{

// implementation
}

Beautiful, isn't it?)

And here is an example of using it, providing only 1,2,5,13,19 params: 

Do ##class(Report.Generator).GenerateReport(
    "Annual Financial Report",     // 1: pTitle
    "Jane Doe",                    // 2: pAuthor
    ,,                             // 3–4 skipped
    "Finance",                     // 5: pDepartment
    ,,,,,,,                        // 6–12 skipped
    "Alice,Bob",                   // 13: pReviewers
    ,,,,,                          // 14–18 skipped
    "Landscape"                    // 19: pOrientation
    // 20 (pOutputPath) omitted -> default used
)

A soup of commas.

There are, of course, many ways to mitigate the problem. E.g., this is where JSON approach can help. I found its usage in the very useful @Benjamin De Boe's bdb-sql-utils lib e.g. here, where qualifiers are being provided as JSON and then parsed inside a method:

do ##class(bdb.sql.InferSchema).BuildAll("/tmp/data-dump/*.csv", { "verbose": 1, "targetSchema": "MySchema" })

I like it and leveraged the same approach in csvgen lib, and introduced the Gen() method, which is an alias to Generate(), but which accepts reasonable 6 parameters instead of 11. Indeed:

Generate signature:

/// generates class for an arbitrary CSV. All the properties are VARCHAR 250
/// fncsv - csv file on disk
/// dlm - delimeter
/// pguessTypes - flag to try to guess on datatypes
/// pclass - class name, if not passed, then will be generated and returned by ref
/// recordCount - amount of records created and returned byRef
/// pverbose=1 - 1 if you want utility to comment to terminal what is going on
/// pappend=0 - 1 if you want to add records to already existing
/// ploaddata=1 - use LOAD DATA if it is avaliable
/// pheader=1 - if equals 1 import skips the 1st row that is considered as header
/// pkey - if non-empty, then will consider to introduce a primary key for a column with the pkey name
ClassMethod Generate(fncsv As %String, dlm As %String = ",", ByRef pclass As %String, ByRef prowtype As %String, pguessTypes As %Boolean = 1, Output recordsCount As %Integer, pverbose As %Boolean = 1, pappend As %Boolean = 0, ploaddata As %Boolean = 1, pheader As %Boolean = 1, pkey As %String = "") As %Status

Gen signature:

///  do ##class(community.csvgen).Gen("/tmp/data-dump/file.csv",,"package.class",,{ "verbose": 1, "guessTypes": 1, "append": 0, "LoadData": 1, "header": 1, "primaryKey": "colname" },.recordsCount)
ClassMethod Gen(fncsv As %String, dlm As %String = ",", ByRef pclass As %String, ByRef prowtype As %String, qualifiers As %String = "", Output recordsCount As %Integer)

Inside the method, JSON is being parsed to a key-value variable, which provides a convenient way to proceed with qualifiers:

ClassMethod FlattenQualifiers(ByRef qf, obj As %DynamicObject, prefix As %String = "") [ Internal, Private ]
{
    set iterator = obj.%GetIterator()
    while iterator.%GetNext(.key, .value) {
        set sub = $s(prefix="":$$$LOWER(key), 1:prefix_"."_$$$LOWER(key))
        if $isobject(value) {
            do ..FlattenQualifiers(.qf, value, sub)
        } else {
            set qf(sub) = value
        }
    }
}

And this is an example of how it can be used, e.g., when Primary needs to be introduced:

Generate call:

set status=##class(community.csvgen).Generate(fn,",",.pclass,.prowtype,1,.tResults,,,,,"name")

I need to type 5 commas before a necessary parameter can be placed.

Gen call:

set status=##class(community.csvgen).Gen(fn,",",.pclass,.prowtype,{"verbose":1,"primarykey":"name"},.tResults)

Here, I provide only the qualifiers I need, and it is clear which are provided. That's why I want to share the experience with you, amazing InterSystems Developer Community!

Thanks again to both @Benjamin De Boe and JSON for making life simpler for developers with ObjectScript!

Happy coding!

Discussion (0)1
Log in or sign up to continue