Nova postagem

Pesquisar

Artigo
· Mar. 27, 2024 6min de leitura

Boosting facial recognition with Vector Search

As you have seen in the latest community publications, InterSystems IRIS has included since version 2024.1 the possibility of including vector data types in its database and based on this type of data vector searches have been implemented. Well, these new features reminded me of the article I published a while ago that was based on facial recognition using Embedded Python.

Introduction

For those of you who don't remember what that article was about, it is linked at the end of this article. The way the application worked was to recognize the faces present in any image and subsequently compare it with the faces that we had already identified in our IRIS instance, indicating who it belonged to.

How could we improve our facial recognition application by taking advantage of these new features? Well, the main problem we found in our project is performance. Having to recalculate the vector for each image in our system and compare it to the image to be identified is a time-consuming process that can be greatly improved with vector search.

Let's see how we could implement it with the Open Exchange project that we have associated.

Example project

The project is going to deploy a container in Docker with the latest released version of InterSystems IRIS (as of the date of publication of this article, 2024.1) in which we will create the table on which we will store the images with which to compare. To do this we will launch the following command from the database:

CREATE TABLE Vectorface_Data.Person (name VARCHAR(50), description VARCHAR(1000), photo VECTOR(DECIMAL, 128))

As you can see, our photo column will be a vector with 128 decimal values. Next we are going to deploy a web application in our instance that we will call from Postman:

This web application will use the Vectorface.WS.Service class, which will be responsible for processing the information received by HTTP POST from Postman.

Let's take a look at said class, first the route map:

XData UrlMap [ XMLNamespace = "https://www.intersystems.com/urlmap" ]
{
<Routes>
	<Route Url="/checkSimilarity" Method="POST" Call="CheckSimilarity" />
	<Route Url="/savePhoto" Method="POST" Call="SavePhoto" />
</Routes>
}

We are going to work with two URLs:

  • /savePhoto: responsible for receiving the photo in base64, analyzing it, vectorizing the image and storing it in the database.
  • /checkSimilarity: which will vectorize the image for comparison and launch the query to the database looking for the most similar image.

Saving photos in IRIS:

ClassMethod SavePhoto() As %Status
{
    Try {
        Do ##class(%REST.Impl).%SetContentType("application/json")
        If '##class(%REST.Impl).%CheckAccepts("application/json") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit
        // Reading the body of the http call with the person data
        set dynamicBody = {}.%FromJSON(%request.Content)

        set dynamicStream = dynamicBody.%Get("fileData",,"stream<base64")

        set stream=##class(%Stream.FileBinary).%New()
        set sc=stream.LinkToFile("/shared/durable/"_dynamicBody.fileName)
        set sc=stream.CopyFromAndSave(dynamicStream)

        set imageVector = ..Checker("/shared/durable/"_dynamicBody.fileName)       
        set imageVector = $REPLACE(imageVector, $CHAR(13,10),",")
        set imageVector = $REPLACE(imageVector,"['","")
        set imageVector = $REPLACE(imageVector,"']","")
        set imageVector = $REPLACE(imageVector,"'","")
        set imageVector = $REPLACE(imageVector," ",",")

        &sql(INSERT INTO Vectorface_Data.Person VALUES (:dynamicBody.name, :dynamicBody.description, TO_VECTOR(:imageVector, DECIMAL)))

        Do ##class(%REST.Impl).%SetStatusCode("200")
        Do ##class(%REST.Impl).%WriteResponse(imageVector)
        return {"result": "Picture stored"}
        
    } Catch (ex) {
        Do ##class(%REST.Impl).%SetStatusCode("400")
        Do ##class(%REST.Impl).%WriteResponse(ex.DisplayString())
        return {"errormessage": "Client error"}
    }
    Quit $$$OK
}

This function transforms the base64 into a file that is subsequently sent to the Python Checker function, responsible for identifying the face and vectorizing it. With the recovered vector we will insert it into our database in vector format.

Comparing vector images with the database

ClassMethod CheckSimilarity() As %Status
{
    Try {
        Do ##class(%REST.Impl).%SetContentType("application/json")
        If '##class(%REST.Impl).%CheckAccepts("application/json") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit
        // Reading the body of the http call with the person data
        set dynamicBody = {}.%FromJSON(%request.Content)

        set dynamicStream = dynamicBody.%Get("fileData",,"stream<base64")

        set stream=##class(%Stream.FileBinary).%New()
        set sc=stream.LinkToFile("/shared/durable/"_dynamicBody.fileName)
        set sc=stream.CopyFromAndSave(dynamicStream)

        set imageVector = ..Checker("/shared/durable/"_dynamicBody.fileName)       
        set imageVector = $REPLACE(imageVector, $CHAR(13,10),",")
        set imageVector = $REPLACE(imageVector,"['","")
        set imageVector = $REPLACE(imageVector,"']","")
        set imageVector = $REPLACE(imageVector,"'","")
        set imageVector = $REPLACE(imageVector," ",",")

        set name = ""
        set similarity = ""
        &sql(SELECT TOP 1 name, similarity INTO :name, :similarity  FROM (SELECT name, VECTOR_DOT_PRODUCT(photo, TO_VECTOR(:imageVector, DECIMAL)) AS similarity FROM Vectorface_Data.Person) ORDER BY similarity DESC)

        set result = {"name": "", "similarity":""}
        set result.name = name
        set result.similarity = similarity
        Do ##class(%REST.Impl).%WriteResponse(result.%ToJSON())

        Do ##class(%REST.Impl).%SetStatusCode("200")	
        
    } Catch (ex) {
        Do ##class(%REST.Impl).%SetStatusCode("400")
        return ex.DisplayString()
    }
    Quit $$$OK
}

The function is very similar, with the exception that we do not record the image that we are going to compare, but rather we use the generated vector to compare it with those registered in the database through an SQL query.

Testing the example

We are going to register a few people with their photos in IRIS, for example... all the General Secretaries of the Communist Party of the Soviet Union because... Who hasn't wanted to know which leader of the Soviet Union they look like?

Here we have an example of our POST call:

As you can see we have the fields that identify Brezhnev with his associated base64 photo. This is how it would look in our table with a vector column:

It's time to launch a test, let's see which Secretary General is most like me:

Creepy...it seems that Stalin is my evil twin...although the similarity is quite low, let's try a different photo of Chernenko, in this case the result should be much higher.

Indeed, here we have a much higher value for Chernenko (0.736), which we already had registered in our database.

Conclusion

The inclusion of the vector type and its associated searches opens up a world of opportunities based on exploiting vector-based AI models, I encourage you all to try it!

PS: My thanks to @Thomas Dyar who provided me with the information necessary to develop this example.

2 Comments
Discussão (2)2
Entre ou crie uma conta para continuar
Anúncio
· Mar. 27, 2024

InterSystems Ideas News #12

Hi Community!

Welcome to the 12th edition of the InterSystems Ideas news! Here's what you can expect from it:

​​​​✓ Call for ideas about InterSystems IRIS Cloud SQL

✓ Get inspired by ideas from the InterSystems Ideas portal

✓ Recently implemented ideas

 

 Many Developer Community members have already tested and used InterSystems' new service, InterSystems IRIS Cloud SQL. You must have bright ideas related to this service, and we are waiting for them in the new Ideas Portal category dedicated to this database-as-a-service (DBaaS). 

 If you are on the lookout for an interesting idea to implement or just want inspiration to create a new application and publish it on the Open Exchange, you can now get to the Ideas Portal directly from the "Applications" item of the Open Exchange main menu.

  Since the launch of the Ideas Portal, 54 ideas related to the InterSystems products and resources for developers have already been implemented.

 
And 12 of them were realized in the first quarter of 2024: 

👏 Thank you for implementing and posting these ideas 👏

Stay tuned for our next announcements!


In the meantime, post your brilliant ideas, and advertise ideas you like to your peers and all Developer Community members!

Discussão (0)1
Entre ou crie uma conta para continuar
Pergunta
· Mar. 26, 2024

VECTOR_COSINE() error help ObjectScript error: <PYTHON EXCEPTION> *<class 'OSError'>: isc_stdout_write: PyArg_ParseTuple failed!

Using VECTOR_COSINE() in SQL query to perform a text similarity search on existing embeddings in a %VECTOR column.

Code is below.

Commented out sql query returns this error: SQLCODE: -29  Field 'NEW_EMBEDDING_STR' not found in the applicable tables^ SELECT TOP ? maxID , activity , outcome FROMMain .AITest ORDER BY VECTOR_COSINE ( new_embedding_str ,

Sql query as written returns ERROR #5002: ObjectScript error: <PYTHON EXCEPTION> *<class 'OSError'>: isc_stdout_write: PyArg_ParseTuple failed!

Table column properties:

Property MaxID As %Integer;

Property Activity As %String(MAXLEN = "");

Property Outcome As %String(MAXLEN = "");

Property Embeddings As %Vector;

Property Total As %String;

Property WorkflowIDs As %String;
ClassMethod GetSimilarity(newWorkflow As %String) [ Language = python, SqlProc ]
{
    import iris

    try:
        libraries = iris.cls('Main.AITest').ImportLibraries()
        if libraries:
            SentenceTransformer = libraries[0]
            model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
        else:
            print("Error: No libraries found.")
            return None

        new_embedding = model.encode(newWorkflow)
        new_embedding_str = ', '.join(map(str, new_embedding))

        sql = "select maxID, activity, outcome from Main.AITest order by VECTOR_COSINE(Embeddings, TO_VECTOR(?, double))"
        # Also tried sql = "select maxID, activity, outcome from Main.AITest order by VECTOR_COSINE(?, TO_VECTOR(Embeddings,         double))"
        params = [new_embedding_str]
        rs = iris.sql.exec(sql, *params)

        if rs.ResultSet._SQLCODE < 0:
            print(f"Loop failed with SQLCODE: {rs.ResultSet._SQLCODE} {rs.ResultSet._Message}")
        elif rs.ResultSet._SQLCODE == 100:
            print("Finished")

    except Exception as e:
            print(f"SQLCODE: {e.sqlcode} {e.message}")
        else:
            print(e)
}
ClassMethod UploadEmbedding(embedding, maxID As %Integer) [ Language = python, SqlProc ]
{
    import iris
    try:
        sql = "UPDATE Main.AITest SET embeddings = (TO_VECTOR(?,double)) WHERE MaxID = ?"
        params = [embedding, maxID]
        iris.sql.exec(sql, *params)

    except Exception as e:
            print(f"SQLCODE: {e.sqlcode} {e.message}")
        else:
            print(e)
}
ClassMethod CreateEmbeddings() [ Language = python, SqlProc ]
{
    import iris
    try:
        libraries = iris.cls('Main.AITest').ImportLibraries()

        if libraries:
            SentenceTransformer = libraries[0]
            model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')

        else:
            print("Error: No libraries found.")
            return None

        select = "select activity, outcome, maxID from Main.AITest"
        where = ""
        condition1 = condition2 = ""
        orderby = ""
        sql = select + " " + where + " " + condition1 + " " + condition2 + " " + orderby

        rs = iris.sql.exec(sql)

        for idx, row in enumerate(rs):

            workflow = row[0] + " " + row[1]
            maxID = row[2]
            embedding = model.encode(workflow)
            embedding_string = ', '.join(map(str, embedding))

            iris.cls('Main.AITest').UploadEmbedding(embedding_string, maxID)

        if rs.ResultSet._SQLCODE < 0:
            print(f"Loop failed with SQLCODE: {rs.ResultSet._SQLCODE} {rs.ResultSet._Message}")
        elif rs.ResultSet._SQLCODE == 100:
            print("Finished")

    except Exception as e:
            print(f"SQLCODE: {e.sqlcode} {e.message}")

        else:
            print(e)
}
10 Comments
Discussão (10)2
Entre ou crie uma conta para continuar
Anúncio
· Mar. 26, 2024

バージョン2024.1:新機能紹介ウェビナー開催決定!(4月~6月)

開発者の皆さん、こんにちは。

(2024/5/30:6月の日時、ウェビナー内容を更新しました)

InterSystems IRIS、InterSystems IRIS for Healthの新バージョン2024.1がリリースされました。

様々な機能の追加や実験的機能としての追加が行われましたが、その中から以下3種類の内容についてウェビナーを開催します!📣

✅4月23日(火)13時半~14時:IRIS 2024.1の管理用Webサーバ(PWS)廃止に備えて

YouTube公開しました👉https://youtu.be/bVwWZt1oNws?list=PLzSN_5VbNaxCeC_ibw2l-xneMCwCVf-Or

✅5月30日(木)13時半~14時:ベクトル検索機能のご紹介

YouTube公開しました👉https://youtu.be/v0G7K2et_Yk?list=PLzSN_5VbNaxB39_H2QMMEG_EsNEFc0ASz

✅6月25日(火)13時半~14時:FHIR新機能のご紹介~2024.1~

オンデマンド配信はこちら👉https://event.on24.com/wcc/r/4597704/ADA161B6446E6BA01623C875CF596FD0
(資料PDFもオンデマンド配信画面よりダウンロードいただけます)
 

ご参考:リリース案内記事

ウェビナーでご案内する機能以外にも実験的機能も含め多くの機能が追加されていますので、以下簡単に概要をご紹介します。

現在日本語ドキュメントは鋭意翻訳中のため、仕上がるまでの間は英語ドキュメント(IRISリリースノートIRIS for Healthリリースノート)をご参照ください。


管理系

✔ マルチボリュームデータベース: スケーラビリティとストレージ管理の強化

下図はデータベース新規作成の画面ですが、設定項目に [New Volume Threshold Size] の項目が追加されました。
(ドキュメント:Configuring a Multivolume Database

この値を 0 より大きい値に設定した場合、DATが指定サイズを超えると指定フォルダ内に IRIS-000x.VOL というデータベースを作ります。(拡張ファイルとして指定サイズで区切ったデータベースファイルが作成できるようになりました)

 

✔ ファスト・オンライン・バックアップ(実験的機能): バックアッププロセスの合理化

以前より、外部バックアップ(Backup.GeneralクラスのExternalFreeze()/ExternalThaw()を利用する方法)と、オンラインバックアップを提供していますが、このリリースの実験的機能追加は「オンラインバックアップ」に対しての高速化とクラスAPIの用意です。(Bakcup.Onlineクラスが追加されました)

現段階では、Backup.Onlineクラスを利用したバックアップに対しての高速化が行われています。管理ポータルメニューからの実行やDBACKルーチンを利用した実行には影響がありません。お試しいただく場合は、Early Access Program(EAP)へのお申込みをどうそよろしくお願いします。

 

✔ 複数のスーパーサーバポート: ネットワーク構成の柔軟性を提供

お馴染みの1972番以外のポート番号をスーパーサーバポートとして追加できるようになりました。(JDBC/ODBC経由のアクセスがある時は別ポートでアクセス、など柔軟に対応できるようになりました)

設定は、管理ポータル > [システム管理] > [セキュリティ] > [Superservers] から行えます。
(ドキュメント:Managing SuperServers

52774のポートを追加した例:

 
JDBCでの接続の例

SQL

✔ JSON_TABLE関数、With句のサポート追加

(ドキュメント:JSON_TABLE (SQL)WITH (SQL))​​​​

JSON_TABLE()関数では、%Net.GetJson()を利用して外部サイトからの結果をそのまま使用することもできます。

例)zipcloudのAPIを使用した例です。

〒0790177で取得できる結果のJSONを使用しています(https://zipcloud.ibsnet.co.jp/api/search?zipcode=0790177

メモ:httpsを使用するため、%Net.GetJson()の第2パラメータに予め管理ポータルで設定したSSL/TLS構成名を指定しています。​

設定は、 [システム管理] > [セキュリティ] > [SSL/TLS構成] メニューを開き、「構成名」に任意名を設定し、「保存」ボタンをクリックします(そのほかの構成パラメータは、デフォルト値で作成します)。

例では、”test” の名称で作成した構成名を指定しています。({"SSLConfigration":"test"})

SELECT address1,address2,address3,kana1,kana2,kana3,zipcode
  FROM JSON_TABLE(%Net.GetJson('https://zipcloud.ibsnet.co.jp/api/search?zipcode=0790177','{"SSLConfiguration":"test"}')
,
  '$.content.results'
    COLUMNS ( address1 VARCHAR(100) PATH '$.address1',
              address2 VARCHAR(100) PATH '$.address2',
              address3 VARCHAR(100) PATH '$.address3',
              kana1 VARCHAR(100) PATH '$.kana1',
              kana2 VARCHAR(100) PATH '$.kana2',
              kana3 VARCHAR(100) PATH '$.kana3',
              zipcode VARCHAR(10) PATH '$.zipcode')
  ) as jt
  ORDER BY address3

《結果》

 

✔ 安全なクエリ中止(CANCEL QUERY)

このリリースでInterSystems SQL に実行中のステートメントを中止し、実行により使用されていたメモリや一時領域などを安全に開放する新しいコマンド CANCEL QUERY が追加されました。

 

クエリパフォーマンスを向上させる機能強化

  • グローバルイテレータでは、ObjectScriptを使用せずに、インデックスおよびデータマップを参照する処理の多くをカーネル内部モジュールで実行します。
  • Adaptive Parallel Execution (APE; アダプティブ並列実行) フレームワークでカラムナーストレージを含むクエリに初めて対応しました。APEでは個別の並列サブクエリをプリペアして実行するのではなく、メインクエリとして同じクエリキャッシュクラスを並列ワーカーコードとして生成します。

 

Embedded Python

✔ Pythonランタイムが選択可能に(Windowsを除く)

Linuxベースの環境限定となりますが、管理ポータルでPythonランタイムを指定できるようになりました。(ドキュメント:Flexible Python Runtime Feature

管理ポータル > [システム管理] > [構成] > [追加の設定] > [メモリ詳細] > [PythonRuntimeLibrary]

PythonRuntimeLibrary構成設定詳細(英語)

 

✔ BPLエディタ内でPythonの記述が可能に

Interoprabilityで使用するビジネス・プロセスのエディタの1つであるBPLエディタ内のCodeアクティビティにEmbedded Pythonが記述できるようになりました。

 

✔ Python WSGI標準準拠のWebアプリが作成可能に(実験的機能)

IRISのインスタンス内部でEmbedded Pythonを使用して flask や Django などの WSGI 準拠フレームワークを実行することができるようになりました。

(ドキュメント:Creating WSGI Applications

WSGIのWebアプリの実行を指定するために、管理ポータルの以下メニューや用意されました。

管理ポータル > [システム管理] > [セキュリティ] > [アプリケーション] > [ウェブ・アプリケーション]

図の設定では、http://Webサーバ/flaskapp が指定されるとFlaskで記述している flasktest アプリケーションが呼び出されます。

メモ:例では、パスを通過した後に付与されるアプリケーション・ロールを指定しています。本来であれば適切なロールを用意し付与しますが簡易的に試すためフルアクセスできる%Allを付与しています)

この例のWebサーバ(localhost:8080)に対してパスを指定すると以下の画面が表示されます。画面でID番号を入力すると、指定IDを条件にIRISにあるテーブルに対してSELECT文が実行されレコードの中身が表示されます。

以下ご参考(コード例):

 
Pythonのコード
 
使用しているHTML
2 Comments
Discussão (2)1
Entre ou crie uma conta para continuar
Artigo
· Mar. 25, 2024 10min de leitura

新しい FHIR サーバープロファイルベースのバリデーション

バージョン 2023.3(InterSystems IRIS for Health)の新機能は、FHIR プロファイル基準の検証を実行する機能です。

(*)

この記事では、この機能の基本的な概要を説明します。

FHIR が重要な場合は、この新機能を絶対にお試しになることをお勧めします。このままお読みください。

Discussão (0)0
Entre ou crie uma conta para continuar