#Artificial Intelligence (AI)

0 フォロワー · 47 投稿

人工知能(AI)は、機械、特にコンピューターシステムによる人間の知能プロセスのシミュレーションです。 これらのプロセスには、学習(情報の取得と情報を使用するためのルール)、推論(概算または明確な結論に到達するためのルールを使用)、および自己修正が含まれます。 詳細はこちら

記事 Toshihiko Minamoto · 10月 1, 2025 6m read

コミュニティの皆さん、こんにちは。
従来のキーワードベースの検索では、ニュアンスのあるドメイン固有のクエリには対応できません。 ベクトル検索であれば、セマンティック認識を利用して、キーワードだけでなくコンテキストにも基づいたレスポンスを AI エージェントで検索して生成することができます。
この記事では、エージェンティック AI RAG(検索拡張生成)アプリケーションを作成手順を紹介します。

実装手順:

  1. エージェントツールを作成する
    • インジェスト機能の追加: ドキュメント(例: InterSystems IRIS 2025.1 リリースノート)を自動的にインジェストしてインデックス作成を行います。
    • ベクトル検索機能の実装
  2. ベクトル検索エージェントを作成する
  3. Triage(メインエージェント)に渡す
  4. エージェントを実行する

1. エージェントツールを作成する
1.1 - 
ドキュメントの取り込みを実装します。ドキュメントの取り込みとインデックス作成を自動化します。

インジェストツールは以下のコードで実装します。

defingestDoc(self):#Check if document is defined, by selecting from table#If not defined then INGEST document, Otherwise back
        embeddings = OpenAIEmbeddings()	
        #Load the document based on the fle type
        loader = TextLoader("/irisdev/app/docs/IRIS2025-1-Release-Notes.txt", encoding='utf-8')      
    documents = loader.load()        
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=<span class="hljs-number">400</span>, chunk_overlap=<span class="hljs-number">0</span>)
    
    texts = text_splitter.split_documents(documents)
                   
    <span class="hljs-comment">#COLLECTION_NAME = "rag_document"</span>
    db = IRISVector.from_documents(
        embedding=embeddings,
        documents=texts,
        collection_name = self.COLLECTION_NAME,
        connection_string=self.CONNECTION_STRING,
    )

    db = IRISVector.from_documents(embedding=embeddings,documents=texts, collection_name = self.COLLECTION_NAME, connection_string=self.CONNECTION_STRING,)</code></pre>

ベクトル検索エージェントは、指定されたリポジトリフォルダから「New in InterSystems IRIS 2025.1」を IRIS Vector Store に自動的に取り込んでインデックスを作成します。この操作はそのデータがまだ存在しない場合にのみ実行されます。


以下のクエリを実行して、ベクトルストアから必要なデータを取得します。

SELECTid, embedding, document, metadata
FROM SQLUser.AgenticAIRAG


1.2 - ベクトル検索機能の実装
以下のコードはエージェントの検索機能を実装します。

defragSearch(self,prompt):#Check if collections are defined or ingested done.# if not then call ingest method
        embeddings = OpenAIEmbeddings()	
        db2 = IRISVector (
            embedding_function=embeddings,    
            collection_name=self.COLLECTION_NAME,
            connection_string=self.CONNECTION_STRING,
        )
        docs_with_score = db2.similarity_search_with_score(prompt)
        relevant_docs = ["".join(str(doc.page_content)) + " "for doc, _ in docs_with_score]
    <span class="hljs-comment">#Generate Template</span>
    template = <span class="hljs-string">f"""
    Prompt: <span class="hljs-subst">{prompt}</span>
    Relevant Docuemnts: <span class="hljs-subst">{relevant_docs}</span>
    """</span>
    <span class="hljs-keyword">return</span> template</code></pre>


Triage エージェントは、受信したユーザークエリを処理し、それを Vector Search Agent に委任します。このエージェントは、セマンティック検索を実行して、最も関連性の高い情報を取得します。


2 - ベクトルストアエージェントを作成する

以下のコードは、以下の要素を含む vector_search_agent を実装します。

  • エージェントを調整するためのカスタム handoff_descriptions
  • 明確な演算 instructions
  • iris_RAG_search ツール(ドキュメントの取り込みとベクトル検索操作に irisRAG.py を使用)
@function_tool    @cl.step(name = "Vector Search Agent (RAG)", type="tool", show_input = False)asyncdefiris_RAG_search():"""Provide IRIS Release Notes details,IRIS 2025.1 Release Notes, IRIS Latest Release Notes, Release Notes"""ifnot ragOprRef.check_VS_Table():
                 #Ingest the document first
                 msg = cl.user_session.get("ragclmsg")
                 msg.content = "Ingesting Vector Data..."await msg.update()
                 ragOprRef.ingestDoc()
        <span class="hljs-keyword">if</span> ragOprRef.check_VS_Table():
             msg = cl.user_session.get(<span class="hljs-string">"ragclmsg"</span>)
             msg.content = <span class="hljs-string">"Searching Vector Data..."</span>
             <span class="hljs-keyword">await</span> msg.update()                 
             <span class="hljs-keyword">return</span> ragOprRef.ragSearch(cl.user_session.get(<span class="hljs-string">"ragmsg"</span>))   
        <span class="hljs-keyword">else</span>:
             <span class="hljs-keyword">return</span> <span class="hljs-string">"Error while getting RAG data"</span>
vector_search_agent = Agent(
        name=<span class="hljs-string">"RAGAgent"</span>,
        handoff_description=<span class="hljs-string">"Specialist agent for Release Notes"</span>,
        instructions=<span class="hljs-string">"You provide assistance with Release Notes. Explain important events and context clearly."</span>,
        tools=[iris_RAG_search]
)</code></pre>


3 - Triage(メインエージェント)に渡す
以下のコードは、処理済みのクエリを Triage エージェント(メインコーディネーター)に渡すためのハンドオフプロトコルを実装します。

 triage_agent = Agent(
        name="Triage agent",
        instructions=(
            "Handoff to appropriate agent based on user query.""if they ask about Release Notes, handoff to the vector_search_agent.""If they ask about production, handoff to the production agent.""If they ask about dashboard, handoff to the dashboard agent.""If they ask about process, handoff to the processes agent.""use the WebSearchAgent tool to find information related to the user's query and do not use this agent is query is about Release Notes.""If they ask about order, handoff to the order_agent."
        ),
        handoffs=[vector_search_agent,production_agent,dashboard_agent,processes_agent,order_agent,web_search_agent]
    )


4 - エージェントを実行する

以下のコードは次の内容を実行します。

  1. ユーザー入力を受け取る
  2. triage_agent  を呼び出す
  3. クエリを Vector_Search_Agent に送信して処理する
@cl.on_message
async def main(message: cl.Message):
    """Process incoming messages and generate responses."""
    # Send a thinking message
    msg = cl.Message(content="Thinking...")
    await msg.send()
agent: Agent = cast(Agent, cl.user_session.get(<span class="hljs-string">"agent"</span>))
config: RunConfig = cast(RunConfig, cl.user_session.get(<span class="hljs-string">"config"</span>))

# Retrieve the chat history from the session.
history = cl.user_session.get(<span class="hljs-string">"chat_history"</span>) or []

# Append the user'<span class="hljs-keyword">s</span> message to the history.
history.append({<span class="hljs-string">"role"</span>: <span class="hljs-string">"user"</span>, <span class="hljs-string">"content"</span>: message.content})

# Used by RAG agent
cl.user_session.<span class="hljs-keyword">set</span>(<span class="hljs-string">"ragmsg"</span>, message.content)
cl.user_session.<span class="hljs-keyword">set</span>(<span class="hljs-string">"ragclmsg"</span>, msg)

<span class="hljs-keyword">try</span>:
    <span class="hljs-keyword">print</span>(<span class="hljs-string">"\n[CALLING_AGENT_WITH_CONTEXT]\n"</span>, history, <span class="hljs-string">"\n"</span>)
    result = Runner.run_sync(agent, history, run_config=config)
           
    response_content = result.final_output
    
    # Update the thinking message with the actual response
    msg.content = response_content
    await msg.update()

    # Append the assistant'<span class="hljs-keyword">s</span> response to the history.
    history.append({<span class="hljs-string">"role"</span>: <span class="hljs-string">"developer"</span>, <span class="hljs-string">"content"</span>: response_content})
    # NOTE: Here we are appending the response to the history <span class="hljs-keyword">as</span> a developer message.
    # This is a BUG in the agents library.
    # The expected behavior is to append the response to the history <span class="hljs-keyword">as</span> an assistant message.

    # Update the session with the <span class="hljs-keyword">new</span> history.
    cl.user_session.<span class="hljs-keyword">set</span>(<span class="hljs-string">"chat_history"</span>, history)
    
    # Optional: Log the interaction
    <span class="hljs-keyword">print</span>(f<span class="hljs-string">"User: {message.content}"</span>)
    <span class="hljs-keyword">print</span>(f<span class="hljs-string">"Assistant: {response_content}"</span>)
    
except Exception <span class="hljs-keyword">as</span> e:
    msg.content = f<span class="hljs-string">"Error: {str(e)}"</span>
    await msg.update()
    <span class="hljs-keyword">print</span>(f<span class="hljs-string">"Error: {str(e)}"</span>)</code></pre>


実際の動作をご覧ください

詳細については、iris-AgenticAI の Open Excahnge アプリケーションページをご覧ください。
以上です

0
0 17
記事 Toshihiko Minamoto · 9月 30, 2025 6m read

この連載記事を終えていなかったことに気付きました!

GIF de Shame On You Meme | Tenor

今日の記事では、フロントエンドから最適なオプションを選択できるように、テキストに最も類似する ICD-10 診断を抽出するプロダクションプロセスについて説明します。

診断の類似度検索:

アプリケーション内で、HL7 で受け取った診断リクエストを示す画面から、医療従事者が入力したテキストに最も近い ICD-10 診断を検索できます。

検索プロセスを高速化するために、HL7 メッセージを取得する際に受信した診断をベクトル化したテキストをデータベースに保存しました。 これを行うために、メッセージから診断コードを抽出し、ベクトルを生成するメソッドにそれを送信する単純な BPL を実装しました。

受信した診断をベクトル化するコードは以下のようになります。

ClassMethod GetEncoding(sentence As %String) As %String [ Language = python ]
{
        import sentence_transformers
        # create the model and form the embeddings
        model = sentence_transformers.SentenceTransformer('/iris-shared/model/')
        embeddings = model.encode(sentence, normalize_embeddings=True).tolist() # Convert search phrase into a vector# convert the embeddings to a stringreturn str(embeddings)
}

こうすることで、診断をベクトル化し、検索するたびにもう一度ベクトル化する必要がなくなります。 このとおり、ダウンロードしたモデルでベクトルを生成するために  sentence_transformer  ライブラリを使用しています。

受信した診断がすべてベクトル化されてデータベースに保存されているため、SELECT クエリを実行するだけで、受信した診断に最も近い ICD-10 診断を抽出できます。

では、ウェブサービスに公開されている、最も類似する 25 件の診断を返すメソッドのコードを見てみましょう。

ClassMethod GetCodeOptions(idRequest As%String) As%Status
{
	set ret = $$$OKtry {
        set sql = 
            "SELECT TOP 25 * FROM (SELECT C.CodeId, C.Description, VECTOR_DOT_PRODUCT(C.VectorDescription, R.VectorDescription) AS Similarity FROM ENCODER_Object.Codes C, ENCODER_Object.CodeRequests R WHERE R.ID = ?) WHERE Similarity > 0.5 ORDER BY Similarity DESC"set statement = ##class(%SQL.Statement).%New()
		$$$ThrowOnError(statement.%Prepare(sql))
        set rs = statement.%Execute(idRequest)

        set array = []
        while rs.%Next() {
            do array.%Push({
                    "CodeId": (rs.%Get("CodeId")),
                    "Description": (rs.%Get("Description")),
                    "Similarity": (rs.%Get("Similarity"))
                })
        }
        set%response.Status = ..#HTTP200OK
        write array.%ToJSON()

    } catch ex {
        set%response.Status = ..#HTTP400BADREQUEST
        return ex.DisplayString()
    }
    quit ret
}

クエリを見てみると、検索を 25 件に制限し、類似度が 0.5 を超える診断に限定しているのがわかります。 類似度計算には、VECTOR_COSINE を使用することも可能ですが、VECTOR_DOT_PRODUCT  メソッドを選択しました。 この場合、今のところこれらに大きな違いは見つかっていません。

検索結果を以下に示します。

LLM を使って診断を特定する

ここまでは、完璧に特定される診断の単純な検索しか行っていませんが、 自由記述形式のテキストから直接診断を特定することは可能でしょうか?

では試してみましょう!

この機能では、選択した LLM モデルに質問を送信する API を提供する Ollama を使用します。 docker-compose.yml ファイルを見ると、Ollama コンテナーの宣言があります。

## llm locally installed  ollama:    build:      context:.      dockerfile:ollama/Dockerfile    container_name:ollama    volumes:    -./ollama/shared:/ollama-shared    ports:      -"11434:11434"

まず、コンテナーデプロイに LLM llama3.2 がダウンロードされることが定義されています。 その理由は? テストでは最も良いパフォーマンスを見せると思ったからです。

これが、コンテナーがデプロイされたときに実行する entrypoint.sh  ファイルのコンテンツとなります。

#!/bin/bash
echo "Starting Ollama server..."
ollama serve &
SERVE_PID=$!

echo "Waiting for Ollama server to be active..."
while ! ollama list | grep -q 'NAME'; do
  sleep 1
done

ollama pull llama3.2

wait $SERVE_PID

Ollama の機能を活用するために、自由形式のテキストを分析する画面を、LLM を使って入力されたテキストから診断を抽出するように変更しました。

また、llama3.2 が診断を直接抽出するために必要なプロンプトを作成するメソッドを追加して、ビジネスプロセスを変更しました。

Method AnalyzeText(text As %String, analysisId As %String, language As %String) As %String [ Language = python ]
{
    import sentence_transformers
    import iris
    import requests
<span class="hljs-keyword">try</span>:
    url = <span class="hljs-string">"http://ollama:11434/api/generate"</span>
    data = {
        <span class="hljs-string">"model"</span>: <span class="hljs-string">"llama3.2"</span>,
        <span class="hljs-string">"prompt"</span>: <span class="hljs-string">"Extrae únicamente los diagnósticos del siguiente texto separándolos por , y sin añadir interpretaciones: "</span>+text,
        <span class="hljs-string">"stream"</span>: <span class="hljs-keyword">False</span>
    }
    response = requests.post(url, json=data)
    analyzedText = response.json()
    
    model = sentence_transformers.SentenceTransformer(<span class="hljs-string">'/iris-shared/model/'</span>)
    phrases = analyzedText[<span class="hljs-string">'response'</span>].split(<span class="hljs-string">","</span>)
    sqlsentence = <span class="hljs-string">""</span>
    <span class="hljs-comment"># iris.cls("Ens.Util.Log").LogInfo("ENCODER.BP.AnalyzeTextProcess", "AnalyzeText", "Starting process")</span>
    <span class="hljs-keyword">for</span> phraseToAnalyze <span class="hljs-keyword">in</span> phrases :
        <span class="hljs-keyword">if</span> phraseToAnalyze != <span class="hljs-string">""</span>:
            embedding = model.encode(phraseToAnalyze, normalize_embeddings=<span class="hljs-keyword">True</span>).tolist()
            sqlsentence = <span class="hljs-string">"INSERT INTO ENCODER_Object.TextMatches (CodeId, Description, Similarity, AnalysisId, RawText) SELECT TOP 50 * FROM (SELECT CodeId, Description, VECTOR_DOT_PRODUCT(VectorDescription, TO_VECTOR('"</span>+str(embedding)+<span class="hljs-string">"', DECIMAL)) AS Similarity, '"</span>+analysisId+<span class="hljs-string">"', '"</span>+phraseToAnalyze+<span class="hljs-string">"' FROM ENCODER_Object.Codes) ORDER BY Similarity DESC"</span>
            stmt = iris.sql.prepare(<span class="hljs-string">"INSERT INTO ENCODER_Object.TextMatches (CodeId, Description, Similarity, AnalysisId, RawText) SELECT TOP 50 * FROM (SELECT CodeId, Description, VECTOR_DOT_PRODUCT(VectorDescription, TO_VECTOR(?, DECIMAL)) AS Similarity, ?, ? FROM ENCODER_Object.Codes) WHERE Similarity &gt; 0.65 ORDER BY Similarity DESC"</span>)                    
            rs = stmt.execute(str(embedding), analysisId, phraseToAnalyze)        
<span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> err:
    iris.cls(<span class="hljs-string">"Ens.Util.Log"</span>).LogInfo(<span class="hljs-string">"ENCODER.BP.AnalyzeTextProcess"</span>, <span class="hljs-string">"AnalyzeText"</span>, repr(err))
    <span class="hljs-keyword">return</span> repr(err)

<span class="hljs-keyword">return</span> <span class="hljs-string">"Success"</span>

}

このプロンプトは非常に単純なものであるため、必要に応じて改善し、調整することも可能ですが、その選択はユーザーにお任せします。 このメソッドはカンマ区切りで LLM レスポンスを取得し、見つかった診断をベクトル化してデータベースに保存します。 以下は結果の例です。

右下の方に、左下のテキストに関して LLM が見つけたものがすべて表示されています。

これで、診断をコーディングするのに役立つ、LLM モデルを使ったアプリケーションが完成しました。 ここで見たように、実装はまったく複雑ではなく、より複雑で包括的なソリューションを構築するための十分な基礎となります。

お読みいただきありがとうございました!

0
0 20
お知らせ Mihoko Iijima · 2月 28, 2025

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

今年最初のプログラミング・コンテスト(USコミュニティ)の開催が決定しました!

🏆 InterSystems AI プログラミングコンテスト:ベクトル検索、生成AI、AIエージェント 🏆

期間:2025年3月17日~4月6日

賞品総額:$12,000 + GlobalSummit2025 へご招待!


0
0 91
記事 Toshihiko Minamoto · 12月 10, 2024 9m read

コミュニティメンバーから、Python 2024 コンテストでの出品に対する非常に素晴らしいフィードバックが届きました。 ここで紹介させていただきます。

純粋な IRIS の 5 倍以上のサイズでコンテナーをビルドしているため、時間がかかっています

コンテナーの始動も時間はかかりますが、完了します

バックエンドは説明通りにアクセス可能です

プロダクションは稼動しています

フロントエンドは反応します

何を説明したいのかがよくわかりません

私以外のエキスパート向けに書かれた説明のようです

出品はこちら: https://openexchange.intersystems.com/package/IRIS-RAG-App

このようなフィードバックをいただけて、本当に感謝しています。プロジェクトに関する記事を書く素晴らしいきっかけとなりました。 このプロジェクトにはかなり包括的なドキュメントが含まれてはいますが、ベクトル埋め込み、RAG パイプライン、LLM テキスト生成のほか、Python や LLamaIndex などの人気の Python ライブラリに精通していることが前提です。

この記事は、IRIS での RAG ワークフローを実証するに当たって、上記の前提事項や、それらが IRIS で RAG ワークフローをこのプロジェクトにどのように適合するかについてを説明する試みです。AI をまったく使用せずに書かれています。

コンテナーが大きいのは、ベクトル埋め込みの作成に関わる Python パッケージに必要なライブラリ依存関係が非常に大きいためです。 より選択的にインポートすることで、サイズを大幅に縮小することが可能です。

コンテナーの初回ビルドには確かに時間がかかりますが、一度ビルドすれば起動時間は短くなります。 とはいえ、起動時間は確かに改善できるかもしれません。 起動にこれほどの時間がかかる主な理由は、アプリケーションのある個所が最後の起動から変更されていることを想定して entrypoint.sh が更新されているためです。これには、データベースの移行、CSS 構成、JavaScript 構成、Python バックエンドコードも含まれており、起動のたびにプロジェクト全体がリコンパイルされます。 これは、このプロジェクトを開発し始めやすくするためで、そうでない場合は、変更が適用されるたびに、フロントエンドとバックエンドのビルドを適切に実行するのが困難になってしまいます。 こうすることで、プロジェクトのコードを変更した場合はコンテナーを再起動し、場合によってはバックエンドのプロダクションを復旧すれば、アプリケーションのインターフェースと操作に変更が反映されます。

バックエンドのプロダクションは、HTTP リクエストを Django アプリケーションに渡すものであり、このパッケージの相互運用性にとって非常に重要であると確信しています。 ただし、私自身は IRIS プラットフォームの初心者であるため、プロダクションについてもっと学ぶ必要があります。

次に、ベクトル埋め込み、LLM、および RAG について、包括的に説明したいと思います。 この内最初に作られたのはベクトル埋め込みです。 まず、ベクトルについて説明します。 ほとんどのコンテキストにおいて、ベクトルは方向です。 空間のどこかを指す矢印です。 より正式には、ベクトルは「大きさだけでなく方向も持つ量」です。 これは、特定の方向へ移動し、空間内の特定の地点で爆発する花火によって例えることができます。 すべての花火が同じ中心点、つまり原点である [0,0,0] から発射され、その原点の周囲に雲となって飛び散るとします。 数学的には、3 つの座標系 [x,y,z] を使用して各花火の爆発の位置を表現することができ、これが花火の爆発の「ベクトル埋め込み」となります。 花火のビデオをたくさん撮影し、花火の爆発をすべてデータセットとして記録すると、花火の一種のベクトル埋め込みデータベース、つまりベクトルストアが作成されることになります。

花火に関する情報を使って何ができるでしょうか? 特定の花火を指して、花火全体の中から同じ点に最も近い位置で爆発した花火について尋ねると、空間の近くの点で爆発した他の花火を検索できます。 最もちかいものを見つけるだけですが、これを行うために数式があります。

花火ごとに、x、y、z の 3 つの数値のみを記録したことに注意してください。3 次元空間において、地上の花火発射台を [0,0,0] としています。

他の特定の花火に対して、距離と時間の両方の観点で最も近く爆発した花火も知りたい場合はどうでしょうか? それを知るには、花火の映像を確認して、各爆発の時間も記録しなければなりません。 これで、4 つの数値を持つ 4 次元ベクトルが取得されました。花火の爆発の 3 次元の位置に爆発の時間を加えたベクトルです。 ベクトル埋め込みにもう 1 つの次元を追加することで、花火の埋め込みがより記述的になりました。

これを機械学習に変換するとどうなるでしょうか? 手短に言えば、大量のテキストデータを処理することで、コンピューター科学者は、フレーズ、文章、段落、またはページなどのテキストを変換し、理論的な高次元空間の点を表現する非常に長い一連の数値に変換できる埋め込みモデルを作成することができました。

4 つの数字ではなく、300、700、さらには 1500 もの数字があります。 これらは、1 つのテキストが互いに「近い」か「遠い」かを 1500 通りまたは1500 次元の意味で表します。 テキストの意味を何らかの方法で表す数字を作成する手段があるというわけですから、多くの人にとって魅力的な概念と言えるでしょう。

数学を使用すると、これらの高次元テキストベクトル埋め込みのうち 2 つを比較して、同じモデルによって作成された場合に、それらが互いにどの程度類似しているか、つまり「近い」かを調べることができます。

このアプリで最初の行われているのが正にこれです。ユーザーはドキュメントを追加して名前を付け、埋め込みのタイプを選択する必要があります。 サーバーはそのドキュメントを受け取り、テキストのチャンクに分割してから、それぞれのチャンクをベクトル埋め込みに変換します。そのチャンクはそのドキュメントの専用のテーブルの行として保存されます。 各ドキュメントは、さまざまなテキスト埋め込みモデルによって作成されるベクトル埋め込みの可変長に対応できるように、それぞれの専用テーブルに保存されます。

ドキュメントがベクトル埋め込みとしてデータベースに保存されると、ユーザーはドキュメントに「尋ねる」クエリを入力できるようになります。 このクエリは 2 つの方法で使用されます。 1 つは、ドキュメントを検索するためです。 従来のテキスト検索は実行せずに、「ベクトル検索」を実行します。 アプリはクエリを受け取り、それをベクトル埋め込みに変換してから、クエリベクトル埋め込みに最も築地する埋め込みのあるドキュメントのセクションを検索します。 各ドキュメントセクションには 0 と 1 の間の類似性スコアが生成され、top_k_similarity と similarity_threshold に基づいて、ベクトルデータベースから複数のセクションが検索されます。 基本的に、取得するドキュメントのセクション数と取得の対象となるためにクエリとどの程度類似している必要があるかを指定することができます。

これが、検索拡張生成における取得です。 次は生成に移りましょう。

コンピューター科学者がテキストを意味的に重要な数値ベクトル埋め込みに変換する方法を見つけると、次に、テキストを生成するモデルの作成に移りました。 これは大きな成功を生み出し、現在では GPT-4、LLama3、Claude 3.5 などの大規模言語モデルとなっています。 これらの LLM はプロンプトまたはクエリを受け取り、補完または回答を提供します。これは LLM が提示されたテキストであるプロンプトから最も続行できる可能性があると考えるテキストです。

LLM は大量のテキストデータに対してトレーニングする必要があり、その回答または補完はそのトレーニングデータに制限されます。 トレーニングセットにないデータを含む可能性のある補完を LLM に提供させる場合、または補完を特定のナレッジセットに基づかせる場合は、1 つの方法として、プロンプトに追加のコンテキストデータを含めることができます。 基本的に、トレーニングされていない内容について LLM から回答を得たい場合、プロンプトに情報を提供する必要があるということです。

多くの人は、ChatGPT やローカルの LLama インストールが自分の個人文書に基づいて回答を提供してくれることを望む状況に陥っていました。 ドキュメント内でその情報を検索し、プロンプトに貼り付けて、質問を入力するだけの単純な操作であり、手作業で行っていました。 それ自体が検索拡張生成です。 RAG は、より正確または利便的な応答を得られるように、ユーザークエリに関連する情報を検索し、LLM にクエリを提供する操作を自動化したに過ぎません。

このアプリでは、ベクトル検索で取得したドキュメントセクションは、インターフェースでモデルとしてラベル付けされている選択された LLM にクエリとともに送信され、回答のコンテキストが提供されます。

このプロジェクト用に制作した動画の例では、シェイクスピアの 2 つの戯曲の全文を含むドキュメント「ハムレット」と「リア王」を使って、「この戯曲の悪役は誰ですか?」と尋ねています。 IRIS データベースには、ハムレットとリア王の 2 つのテーブルがすでに存在します。 各テーブルには、各戯曲のテキストをセクションに分割して作成されたベクトル埋め込みの行が入力されています。 これらの埋め込みは、一連の長い数値によって各ドキュメントセクションの多次元を表現しています。

サーバーは、「この戯曲の悪役は誰ですか」という質問を、リア王のベクトル埋め込みを生成した Text-to-Vector モデルを使用して数値ベクトルに変換し、リア王テーブル内でそれに最も類似するセクションを見つけます。 これらはおそらく悪役という語が言及されたセクションかもしれませんが、悪役が明示的に言及されていない場合でも、裏切り、裏切り、欺瞞などの他の悪役についても言及されている可能性があります。 こういったドキュメントのセクションは、クエリに追加され、合わせてプロンプトとして LLM に送信されます。LLM は提供されたドキュメントのセクションに基づいて質問に回答します。

これはドキュメントごとに個別に実行されるため、クエリの回答はクエリされているドキュメントに応じて異なります。 これにより頭字語が補完されます。ベクトル検索の力を使用して関連するコンテキスト情報を取得することで、LLM からの応答の生成を強化しているためです。

この記事をお読みいただきありがとうございました。このトピックについては今後の記事でも発展させたいと思います。 フィードバックをお待ちしています。

0
0 82
記事 Toshihiko Minamoto · 12月 4, 2024 5m read

前回の記事では、ICD-10 による診断のコーディングをサポートできるように開発された d[IA]gnosis アプリケーションを紹介しました。 この記事では、InterSystems IRIS for Health が、事前トレーニングされた言語モデル、そのストレージ、およびその後の生成されたすべてのベクトルの類似性の検索を通じて ICD-10 コードのリストからベクトルを生成するために必要なツールをどのように提供するかを見ていきます。

はじめに

AI モデルの開発に伴って登場した主な機能の 1 つは、RAG(検索拡張生成)という、コンテキストをモデルに組み込むことで LLM モデルの結果を向上させることができる機能です。 この例では、コンテキストは ICD-10 診断のセットによって提供されており、これらを使用するには、まずこれらをベクトル化する必要があります。

診断リストをベクトル化するにはどうすればよいでしょうか?

SentenceTransformers と Embedded Python

ベクトルを生成するために、トレーニング済みのモデルからの自由テキストのベクトル化を大幅に容易にする SentenceTransformers という Python ライブラリを使用しました。 そのウェブサイトでは以下のように説明されています。

Sentence Transformers(別名: SBERT)は、最先端のテキスト画像埋め込みモデルへのアクセス、使用、およびトレーニングに使用される一般的な Python モジュールです。 SentenceTransformer モデルを使って埋め込みを計算(クイックスタート)するか、Cross-Encoder モデルを使って類似性スコアを計算(クイックスタート)するために使用できます。 これにより、セマンティック検索セマンティックテキスト類似性パラフレーズマイニングなどの広範なアプリケーションが可能になります。

SentenceTransformers コミュニティが開発した全モデルの中で、786 の次元ベクトルを生成する BioLORD-2023-M というトレーニング済みモデルを見つけました。

このモデルは、臨床文章や生物医学的概念の意味のある表現を生成するための新しい事前トレーニング戦略である BioLORD を使用してトレーニングされました。

最先端の方法は、同じ概念を指す名前の表現の類似性を最大化し、対照学習を通じて崩壊を防ぐことによって機能します。 ただし、生物医学的名前は必ずしも自明ではないため、非意味的な表現になることがあります。

BioLORD は、定義を使用した概念表現と、生物医学オントロジーで構成されるマルチリレーショナルナレッジグラフから得られる短い説明を基礎にすることで、この問題を克服しています。 この基礎により、このモデルは、オントロジーの階層構造により密接に一致する、より意味論的な概念表現を生成します。 BioLORD-2023 は、臨床文章(MedSTS)と生物医学概念(EHR-Rel-B)の両方でテキスト類似性の新たな最先端を確立しています。

この定義でわかるように、このモデルは、ICD-10 コードと自由テキストの両方をベクトル化するときに役立つ医療概念で事前トレーニングされています。

このプロジェクトでは、このモデルをダウンロードして、ベクトルの作成を高速化します。

if not os.path.isdir('/shared/model/'):
    model = sentence_transformers.SentenceTransformer('FremyCompany/BioLORD-2023-M')            
    model.save('/shared/model/')

ダウンロードしたら、ベクトル化するテキストをリストに入力して、プロセスを高速化します。以前に ENCODER.Object.Codes クラスに記録した ICD-10 コードをベクトル化する方法を見てみましょう。

st = iris.sql.prepare("SELECT TOP 50 CodeId, Description FROM ENCODER_Object.Codes WHERE VectorDescription is null ORDER BY ID ASC ")
resultSet = st.execute()
df = resultSet.dataframe()

if (df.size > 0): model = sentence_transformers.SentenceTransformer("/shared/model/") embeddings = model.encode(df['description'].tolist(), normalize_embeddings=True)

df[<span class="hljs-string">'vectordescription'</span>] = embeddings.tolist()

stmt = iris.sql.prepare(<span class="hljs-string">"UPDATE ENCODER_Object.Codes SET VectorDescription = TO_VECTOR(?,DECIMAL) WHERE CodeId = ?"</span>)
<span class="hljs-keyword">for</span> index, row <span class="hljs-keyword">in</span> df.iterrows():
    rs = stmt.execute(str(row[<span class="hljs-string">'vectordescription'</span>]), row[<span class="hljs-string">'codeid'</span>])

else: flagLoop = False

ご覧のとおり、CSV ファイルから抽出した後に前のステップで登録した、まだベクトル化されていないコードを先に抽出し、次に、ベクトル化する記述のリストを抽出します。Python の sentence_transformers ライブラリを使用してモデルを復元し、関連する埋め込みを生成します。

最後に、UPDATE を実行して、ベクトル化された記述で ICD-10 コードを更新します。 ご覧のように、モデルが返した結果をベクトル化するコマンドは、IRIS における SQL コマンドの TO_VECTOR です。

IRIS で使用する

Python コードの準備ができたので、Ens.BusinessProcess を拡張するクラスにラップして、プロダクションに含めましょう。次に、CSV ファイルを取得するビジネスサービスに接続すれば、完成です!

プロダクションでこのコードがどのように見えるかを確認しましょう。

ご覧のように、EnsLib.File.InboundAdapter アダプターを備えたビジネス サービスにより、コードファイルを収集し、それをすべてのベクトル化とストレージ操作を実行するビジネスプロセスにリダイレクトできます。すると、以下のようなレコードセットが得られます。

これで、アプリケーションは、送信するテキストに一致する可能性のある項目を検索できるようになりました!

次回の記事では...

次回の記事では、Angular 17 で開発されたアプリケーションのフロントエンドが IRIS for Health のプロダクションとどのように統合されるか、および IRIS が分析するテキストをどのように受け取り、それらをベクトル化し、ICD-10 コードテーブルで類似性を検索するかを説明します。

お見逃しなく!

0
0 83
記事 Toshihiko Minamoto · 11月 26, 2024 5m read

ベクトルデータ型と Vector Search 機能が IRIS に導入されたことにより、アプリケーションの開発に多数の可能性が開かれました。こういったアプリケーションの例として、バレンシア保健省が AI モデルを使用した ICD-10 コーディング支援ツールを要求した公募で出品されたアプリケーションが最近私の目に留まりました。

要求されたツールのようなアプリケーションをどのように実装できるでしょうか? 必要なものを確認しましょう。

  1. ICD-10 コードのリスト。自由テキスト内で診断を検索するための RAG アプリケーションのコンテキストとして使用します。
  2. ICD-10 コード内で相当するものを検索するためにテキストをベクトル化するトレーニング済みモデル。
  3. ICD-10 コードとテキストの取り込みとベクトル化を行うために必要な Python ライブラリ。
  4. 可能性のある診断を見つけるためのテキストを受け入れる使いやすいフロントエンド。
  5. フロントエンドから受信するリクエストのオーケストレーション。

これらのニーズに対応するために、IRIS は何を提供できるでしょうか?

  1. CSV インポート。RecordMapper 機能を使うか、Embedded Python を直接使用します。
  2. Embedded Python によって、選択されたモデルを使ってベクトルを生成するために必要な Python コードを実装できます。
  3. フロントエンドアプリケーションから呼び出される REST API の公開。
  4. IRIS 内で情報を追跡できる相互運用性プロダクション。

では、開発済みの例を確認しましょう。

d[IA]gnosis

この記事に関連して、開発済みのアプリケーションにアクセスできます。次の記事では、モデルの使用、ベクトルのストレージ、ベクトル検索の使用に至るまで、各機能をどのように実装するかを詳しく説明します。

アプリケーションを確認しましょう。

ICD-10 コードをインポートする

構成画面に、CSV ファイルのフォーマットはインポートしようとしている ICD-10 コードに準拠している必要があると表示されます。 読み取りとベクトル化のプロセスは多数のリソースを使用し、長時間かかるため、Docker コンテナーのデプロイによって、Docker が使用できる RAM メモリだけでなく、要件が割り当てられた RAM を超過する場合に備えてディスクメモリも構成されます。

# iris  iris:    init:true    container_name:iris    build:      context:.      dockerfile:iris/Dockerfile    ports:      -52774:52773      -51774:1972    volumes:    -./shared:/shared    environment:    -ISC_DATA_DIRECTORY=/shared/durable    command:--check-capsfalse--ISCAgentfalse    mem_limit:30G    memswap_limit:32G

ICD-10 コードを含むファイルは、プロジェクトパス /shared/cie10/icd10.csv にあります。100% に達すスト、アプリケーションを使用できます。

私たちのアプリケーションでは、診断コーディング用に 2 つの異なる機能を定義しました。1 つはシステムで受信した HL7 メッセージに基づいたもので、もう 1 つは自由テキストに基づいたものです。

HL7 による診断のキャプチャ

プロジェクトにはテスト用に準備された HL7 メッセージが含まれており、/shared/hl7/messagesa01_en.hl7 ファイルを /shared/HL7In フォルダにコピーすれば、関連付けられたプロダクションが診断を抽出してウェブアプリケーションに表示します。

診断リクエスト画面では、HL7 メッセージングで受信したすべての診断を確認できます。 これを ICD-10 にコーディングするには、虫眼鏡をクリックするだけで、受信した診断に最も近い ICD-10 コードのリストを表示することができます。

選択すると、リストに診断とそれに関連付けられた ICD-10 コードが表示されます。 封筒のアイコンが付いたボタンをクリックすると、元のメッセージと、診断セグメント内で選択した新しいメッセージによって、メッセージが生成されます。

MSH|^~\&|HIS|HULP|EMPI||||ADT^A08|592956|P|2.5.1
EVN|A01|
PID|||1556655212^^^SERMAS^SN~922210^^^HULP^PI||GARCÍA PÉREZ^JUAN^^^||20150403|M|||PASEO PEDRO ÁLVAREZ 1951 CENTRO^^LEGANÉS^MADRID^28379^SPAIN||555283055^PRN^^JUAN.GARCIA@YAHOO.COM|||||||||||||||||N|
PV1||N
DG1|1||O10.91^Unspecified pre-existing hypertension complicating pregnancy^CIE10-ES|Gestational hypertension||A||

このメッセージは /shared/HL7Out パスにあります。

自由テキストによる診断のキャプチャ

テキストアナライザーオプションでは、分析プロセスが実行される自由テキストを含めることができます。 アプリケーションは、見出し語化された 3 つの単語のタプルを検索します(冠詞、代名詞、およびその他の関連性の低い単語は削除されます)。 分析が完了すると、システムは関連する下線付きのテキストと、考えられる診断を表示します。

分析の実行が完了したら、その内容は分析履歴からいつでも参照できます。

分析履歴

実行されたすべての分析は記録され、いつでも参照でき、利用可能なすべての ICD-10 コードを表示できます。

次回の記事では...

Embedded Python を使用して、コンテキストとして使用する ICD-10 コードと自由テキストの両方のベクトル化に特定の LLM モデルを使用する方法を見ていきます。

ご質問やご提案がありましたら、ぜひこの記事のコメントでお知らせください。

0
0 63
お知らせ Mihoko Iijima · 9月 4, 2024

コミュニティの皆さん、こんにちは!

開発者コミュニティ AI 懸賞企画 お楽しみいただけましたか? まだまだ改良の必要がありそうですが、DC AIを利用して何か新しい回答が得られていることを願っています。

この投稿では、優勝者を発表します!🎊(抽選の様子を動画でご紹介しています。ぜひご覧ください!)

4
0 93
ディスカッション Toshihiko Minamoto · 7月 26, 2024

コミュニティの皆さん
こんにちは!

ご存知かもしれませんが、Developer Community AIがリリースされて1ヶ月以上が経ちました🎉 興味を持たれた方は、ぜひ試してみてください😁 まだの方は、ぜひお試しください!いずれにせよ、まだベータ版であるため、私たちは皆さんがこのAIについてどのように考えているかを知りたいと思っています。

皆さんの時間と労力を大切にしているので、感想をシェアしてくれたメンバーに、抽選でキュートな賞品をプレゼントします。この懸賞に参加するには、以下のガイドラインに従ってください。

3
0 105
記事 Toshihiko Minamoto · 4月 18, 2024 13m read

我々には、Redditユーザーが書いた、おいしいレシピデータセット がありますが, 情報のほとんどは投稿されたタイトルや説明といったフリーテキストです。埋め込みPythonLangchainフレームワークにあるOpenAIの大規模言語モデルの機能を使い、このデータセットを簡単にロードし、特徴を抽出、分析する方法を紹介しましょう。

データセットのロード

まず最初に、データセットをロードするかデータセットに接続する必要があります。

これを実現するにはさまざまな方法があります。たとえばCSVレコードマッパーを相互運用性プロダクションで使用したり csvgenのようなすばらしい OpenExchange アプリケーションを使用することもできます。

今回、外部テーブルを使用します。これは物理的に別の場所に保存されているデータをIRIS SQLで統合する非常に便利な機能です。

まずは外部サーバ(Foreign Server)を作成します。

CREATE FOREIGN SERVER dataset FOREIGN DATA WRAPPER CSV HOST '/app/data/'

その上でCSVファイルに接続する外部テーブルを作成します。

CREATE FOREIGN TABLE dataset.Recipes (
  CREATEDDATE DATE,
  NUMCOMMENTS INTEGER,
  TITLE VARCHAR,
  USERNAME VARCHAR,
  COMMENT VARCHAR,
  NUMCHAR INTEGER
) SERVER dataset FILE 'Recipes.csv' USING
{
  "from": {
    "file": {
       "skip": 1
    }
  }
}

以上です。すぐに「dataset.Recipes」にSQLクエリを実行できます。 image

## どんなデータが必要?

データセットは興味深く、直ぐに処理したいと思うのですが、調理のレシピを決めたいのであれば、分析に使える情報がもう少し必要です。 2つの永続化クラス(テーブル)を使用します。

  • yummy.data.Recipe抽出分析したいレシピのタイトルと説明、他のプロパティが入ったクラス (例: スコア、難易度、材料、調理タイプ、準備時間)
  • yummy.data.RecipeHistory レシピのログを取るためのシンプルなクラス

これで 「yummy.data*」 テーブルにデータセットの内容をロードすることができます。

do ##class(yummy.Utils).LoadDataset()

一見良さそうに見えますが、スコア、難易度、材料、準備時間、調理時間フィールドのデータをどのように生成するのかを見つける必要があります。

## レシピの分析 各レシピのタイトルと説明を処理します

  • 難易度, 材料, 調理タイプなどの抽出
  • 何を作りたいか決められるよう、基準に基づいて独自のスコアを構築

以下を使用します

  • より多くの分析を構築したい場合に再利用できる一般的な分析構造

LLM(大規模言語モデル)は自然言語を処理するための本当に素晴らしいツールです。

LangChainはPythonで動くようになっているので、Embedded Pythonを使ってInterSystems IRISで直接使うことができます。 LangChain is ready to work in Python, so we can use it directly in InterSystems IRIS using Embedded Python.

完全な SimpleOpenAI クラスは以下のようになります。

/// レシピ向けのシンプルな OpenAI 分析
Class yummy.analysis.SimpleOpenAI Extends Analysis
{

Property CuisineType As %String;

Property PreparationTime As %Integer;

Property Difficulty As %String;

Property Ingredients As %String;

/// 実行
/// ターミナルから実行できます。
/// set a = ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(8))
/// do a.Run()
/// zwrite a
Method Run()
{
    try {
        do ..RunPythonAnalysis()

        set reasons = ""

        // 好きな調理タイプ
        if "spanish,french,portuguese,italian,korean,japanese"[..CuisineType {
            set ..Score = ..Score + 2
            set reasons = reasons_$lb("It seems to be a "_..CuisineType_" recipe!")
        }

        // 丸一日調理に費やしたくない :)
        if (+..PreparationTime < 120) {
            set ..Score = ..Score + 1
            set reasons = reasons_$lb("You don't need too much time to prepare it") 
        }
        
        // 好きな材料ボーナス
        set favIngredients = $listbuild("kimchi", "truffle", "squid")
        for i=1:1:$listlength(favIngredients) {
            set favIngred = $listget(favIngredients, i)
            if ..Ingredients[favIngred {
                set ..Score = ..Score + 1
                set reasons = reasons_$lb("Favourite ingredient found: "_favIngred)
            }
        }

        set ..Reason = $listtostring(reasons, ". ")

    } catch ex {
        throw ex
    }
}

/// 分析結果でレシピを更新する
Method UpdateRecipe()
{
    try {
        // 親クラスの処理を先に呼び出す
        do ##super()

        // 個別のOpenAI 解析結果を追加
        set ..Recipe.Ingredients = ..Ingredients
        set ..Recipe.PreparationTime = ..PreparationTime
        set ..Recipe.Difficulty = ..Difficulty
        set ..Recipe.CuisineType = ..CuisineType

    } catch ex {
        throw ex
    }
}

/// 埋め込み Python + Langchain で分析を実行
/// do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(8)).RunPythonAnalysis(1)
Method RunPythonAnalysis(debug As %Boolean = 0) [ Language = python ]
{
    # load OpenAI APIKEY from env
    import os
    from dotenv import load_dotenv, find_dotenv
    _ = load_dotenv('/app/.env')

    # account for deprecation of LLM model
    import datetime
    current_date = datetime.datetime.now().date()
    # date after which the model should be set to "gpt-3.5-turbo"
    target_date = datetime.date(2024, 6, 12)
    # set the model depending on the current date
    if current_date > target_date:
        llm_model = "gpt-3.5-turbo"
    else:
        llm_model = "gpt-3.5-turbo-0301"

    from langchain.chat_models import ChatOpenAI
    from langchain.prompts import ChatPromptTemplate
    from langchain.chains import LLMChain

    from langchain.output_parsers import ResponseSchema
    from langchain.output_parsers import StructuredOutputParser

    # init llm model
    llm = ChatOpenAI(temperature=0.0, model=llm_model)

    # prepare the responses we need
    cuisine_type_schema = ResponseSchema(
        name="cuisine_type",
        description="What is the cuisine type for the recipe? \
                     Answer in 1 word max in lowercase"
    )
    preparation_time_schema = ResponseSchema(
        name="preparation_time",
        description="How much time in minutes do I need to prepare the recipe?\
                     Anwer with an integer number, or null if unknown",
        type="integer",
    )
    difficulty_schema = ResponseSchema(
        name="difficulty",
        description="How difficult is this recipe?\
                     Answer with one of these values: easy, normal, hard, very-hard"
    )
    ingredients_schema = ResponseSchema(
        name="ingredients",
        description="Give me a comma separated list of ingredients in lowercase or empty if unknown"
    )
    response_schemas = [cuisine_type_schema, preparation_time_schema, difficulty_schema, ingredients_schema]

    # get format instructions from responses
    output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
    format_instructions = output_parser.get_format_instructions()
    
    analysis_template = """\
    Interprete and evaluate a recipe which title is: {title}
    and the description is: {description}
    
    {format_instructions}
    """
    prompt = ChatPromptTemplate.from_template(template=analysis_template)

    messages = prompt.format_messages(title=self.Recipe.Title, description=self.Recipe.Description, format_instructions=format_instructions)
    response = llm(messages)

    if debug:
        print("======ACTUAL PROMPT")
        print(messages[0].content)
        print("======RESPONSE")
        print(response.content)

    # populate analysis with results
    output_dict = output_parser.parse(response.content)
    self.CuisineType = output_dict['cuisine_type']
    self.Difficulty = output_dict['difficulty']
    self.Ingredients = output_dict['ingredients']
    if type(output_dict['preparation_time']) == int:
        self.PreparationTime = output_dict['preparation_time']

    return 1
}

}

「RunPythonAnalysis」メソッドがOpenAIが詰め込むところです :)  ターミナルから直接実行してレシピを受け取れます。

do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12)).RunPythonAnalysis(1)

以下のような出力を受け取れます。

USER>do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12)).RunPythonAnalysis(1)
======実際の課題
                    レシピタイトルを解釈、評価: 巻きずし - アラスカロール
                    説明: 寿司がたべたいのに巻きすがない? 代わりに簡単なバージョンを試してみてください。超簡単なのに、同じようにおいしい!
[Video Recipe](https://www.youtube.com/watch?v=1LJPS1lOHSM)
# 材料
提供量:  \~サンドイッチ5枚分
* 米1カップ
* 水 3/4 カップ + 大さじ 2 1/2
* 昆布 小口切り 1枚
* 米酢 大さじ2
* さとう 大さじ1
* 塩 小さじ1
* アボカド 2個
* カニカマ 6個
* 和風マヨ 大さじ2
* サーモン 1/2 ポンド  
# レシピ     
* 酢飯1合をボウルに入れ、2回以上、または水が透明になるまで米を洗う。炊飯器に米を移し、昆布の小口切り1枚と水3/4カップ+大さじ2と1/2杯を加える。炊飯器の指示に従って炊く。
* 米酢大さじ2、砂糖大さじ1、塩小さじ1を中くらいのボウルに入れる。全体がよく混ざるまで混ぜる。
* 炊き上がったら昆布を取り除き、すぐに酢を入れた中ボウルに米をすべてすくい入れ、飯ベラを使ってよく混ぜる。米をつぶさないように、切るように混ぜること。炊きあがったら、キッチンタオルをかけて室温まで冷ます。
* アボカド1個の上部を切り、アボカドの中央に切り込みを入れ、ナイフに沿って回転させる。次にアボカドを半分ずつ取り、ひねる。その後、ピットのある側を取り、慎重にピットに切り込みを入れ、ひねって取り除く。その後、手で皮をむく。この手順をもう片方のアボカドでも繰り返す。作業スペースを確保するため、作業台を片付けるのを忘れずに。次に、アボカドを下向きに置き、薄くスライスする。スライスしたら、ゆっくりと広げていく。それが終わったら、脇に置いておく。
* カニカマから包みをはずす。カニカマを縦にむいていく。すべてのカニカマを剥いたら、横に回転させながら細かく刻み、和風マヨ(大さじ2)とともにボウルに入れ、全体がよく混ざるまで混ぜる。
* 鋭利なナイフを斜めに入れ、木目に逆らって薄くスライスする。切り口の厚さは好みによる。ただ、すべてのピースが同じような厚さになるようにする。
* 海苔巻きラップを手に取る。キッチンバサミを使い、海苔巻きラップの半分の位置から切り始め、ラップの中心を少し過ぎるまで切る。ラップを垂直に回転させ、作り始める。すし飯を握るために、手に水をつけておく。酢飯を手に取り、海苔巻きの左上の四辺に広げる。次に、右上にサーモンを2切れ並べる。右下にアボカドを2切れのせる。最後に左下にカニサラダを小さじ2杯ほどのせる。次に、右上の四つ角を右下の四つ角に折り込み、さらに左下の四つ角に折り込む。最後に、左上の四つ角をサンドイッチの残りの部分に折り込む。その後、ラップを上に置き、半分に切って、生姜とわさびを2、3枚添えれば出来上がり。
                    
                    出力は、先頭と末尾の"``json "と"``"を含む、以下のスキーマでフォーマットされたマークダウンのコードスニペットでなければなりません:
json
{
        "cuisine_type": string  // レシピの調理タイプは?                                  小文字の1単語で回答
        "preparation_time": integer  // レシピの準備に必要な時間(分)は? 整数で回答(不明な場合はnull)
        "difficulty": string  // レシピの難易度は?                               「容易」「標準」「難しい」「とても難しい」のうちから1つを回答
        "ingredients": string  // 小文字のカンマ区切りの材料リスト、不明な場合は空
}

                    
======応答
json
{
        "cuisine_type": "japanese",
        "preparation_time": 30,
        "difficulty": "easy",
        "ingredients": "sushi rice, water, konbu, rice vinegar, sugar, salt, avocado, imitation crab sticks, japanese mayo, salmon"
}

良さそうです。OpenAIのプロンプトは有用な情報を返してくれるようです。ターミナルから分析クラス全体を実行してみましょう:

set a = ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12))
do a.Run()
zwrite a
USER>zwrite a
a=37@yummy.analysis.SimpleOpenAI  ; <OREF>
+----------------- general information ---------------
|      oref value: 37
|      class name: yummy.analysis.SimpleOpenAI
| reference count: 2
+----------------- attribute values ------------------
|        CuisineType = "japanese"
|         Difficulty = "easy"
|        Ingredients = "sushi rice, water, konbu, rice vinegar, sugar, salt, avocado, imitation crab sticks, japanese mayo, salmon"
|    PreparationTime = 30
|             Reason = "It seems to be a japanese recipe!. You don't need too much time to prepare it"
|              Score = 3
+----------------- swizzled references ---------------
|           i%Recipe = ""
|           r%Recipe = "30@yummy.data.Recipe"
+-----------------------------------------------------

## 全レシピを解析する!

当然、読み込んだすべてのレシピで分析を実行したいでしょう。 この方法でレシピ ID の範囲を分析することができます

USER>do ##class(yummy.Utils).AnalyzeRange(1,10)
> Recipe 1 (1.755185s)
> Recipe 2 (2.559526s)
> Recipe 3 (1.556895s)
> Recipe 4 (1.720246s)
> Recipe 5 (1.689123s)
> Recipe 6 (2.404745s)
> Recipe 7 (1.538208s)
> Recipe 8 (1.33001s)
> Recipe 9 (1.49972s)
> Recipe 10 (1.425612s)

その後、レシピテーブルを再度表示させ、結果をチェックします。

select * from yummy_data.Recipe

image

どんぐりカボチャのピザか、豚肉入り韓国風豆腐キムチを試してみます:) いずれにせよ、家で再確認する必要がありますね :)

最後に

サンプルソースは全て https://github.com/isc-afuentes/recipe-inspector にあります。

この簡単な例で、InterSystems IRIS で LLM テクニックを使用して機能を追加したり、データの一部を分析する方法を学びました。

これを起点に以下のことが考えられます

  • InterSystems BIを使い、キューブやダッシュボードでデータの検索やナビゲートをおこなう。
  • Webアプリを作成し、UIを提供する(例:Angular)RESTForms2のようなパッケージを活用することで、永続クラスへのREST APIを自動的に生成することができます。 *レシピが好きか嫌いかを保存し、新しいレシピが好きかどうかを判断するのはいかがでしょうか。IntegratedMLアプローチ、あるいはLLMアプローチでいくつかの例データを提供し、RAG(Retrieval Augmented Generation)ユースケースを構築してみるのも良いでしょう。

他にどんなことが試せそうでしょうか?ご意見をお聞かせください!

0
0 458
お知らせ Mihoko Iijima · 4月 9, 2024

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

(2024.4.17更新:コンテストタイトルに「ベクトル検索」を追加しました)

次の InterSystems オンラインプログラミングコンテストのテーマが決定しました!👉生成 AI、ベクトル検索、機械学習 です!

🏆 InterSystems ベクトル検索、GenAI、ML コンテスト(USコミュニティ) 🏆

期間: 2024年4月22日~5月19日 

賞金総額: $14,000


0
0 125
記事 Toshihiko Minamoto · 4月 8, 2024 10m read

 

人工知能は、命令によってテキストから画像を生成したり、単純な指示によって物語を差作成したりすることだけに限られていません。

多様な写真を作成したり、既存の写真に特殊な背景を含めたりすることもできます。

また、話者の言語や速度に関係なく、音声のトランスクリプションを取得することも可能です。

では、ファイル管理の仕組みを調べてみましょう。

問題

入力値としてファイルが必要なメソッドについて OpenAI 情報を分析する場合、multipart/form-data を使用してパラメーターを指定する必要があります。

IRIS では、JSON コンテンツを使って POST メソッドへの呼び出しを作成する方法をすでに知っていますが、 この場合、Base64 フォーマットのファイルコンテンツを持つパラメーターを使用するのは非現実的です。

multipart/form-data にファイルコンテンツを含めるには、クラス %Net.MIMEPart を使用する必要があります。

呼び出しにファイルを含めるには、クラスオブジェクト %Net.MIMEPart に関連付けられた Content-Disposition ヘッダーを作成します。

set content = ##class(%Net.MIMEPart).%New()
set contentDisposition = "form-data; name="_$CHAR(34)_"image"_$CHAR(34)
set contentDisposition = contentDisposition_"; filename="_$CHAR(34)_fileName_$CHAR(34)
do content.SetHeader("Content-Disposition",contentDisposition)

Request クラスを使用してプロセスの値を保持するため、Base64 コンテンツを、コンテンツのボディを構成するストリームに変換する必要があります。

StreamUtils ユーティリティを使用して、Base64 を Stream に変換できます。

注意: 「pImage」変数には、ファイルコンテンツの Base64 文字列が含まれます。

Do##class(HS.Util.StreamUtils).Base64Encode(pImage, .tStream)
Set content.Body = tStream

ただし、Global Summit 2023 において、幸運にも InterSystems エキスパートからもっと有用なコツを得られました。 実行は StreamUtils よりも高い効果があり、最終的には String と Stream のレコードを読み取るループがあることを教えていただきました。

これは、JSON を使用してそれを Stream に変換する Get を実行するだけの単純なソリューションです。

set contentfile = {}
set contentfile.file = pImage
set content.Body = contentfile.%Get("file",,"stream<base64")

呼び出しに必要なすべてのパラメーターを含めたら、ようやくパーツをエンクローズする新しい MIMEPart クラスを作成できます。

Set rootMIME = ##class(%Net.MIMEPart).%New()
do rootMIME.Parts.Insert(content)
set writer = ##class(%Net.MIMEWriter).%New()
set tSC = writer.OutputToStream(tHttpRequest.EntityBody)
set tSC = writer.WriteMIMEBody(rootMIME)
Set tContentType = "multipart/form-data; boundary="_rootMIME.Boundary
set tSC = ..Adapter.SendFormDataArray(.tHttpResponse, "POST", tHttpRequest,,,url)

このようにして、OpenAI で必要なメソッドにファイルコンテンツを送信します。

画像ファイル

Image メソッドでは、写真を送信してバリエーションを実行することができます。 すべてのイラストは PNG 形式である必要があるため、Base64 形式のファイルコンテンツを指定する場合、ファイル名はランダムに生成され、PNG 拡張子が付けられます。

以下は、写真の変更例です。

元の写真バリエーション

ご覧のように、プログラムは命令を独自に解釈しています。

会社ロゴは円形と判断して入れ替えています。 また、オフィスにはガラスのドアが使用されていることを認識し、別のガラスのドアに入れ替えて、この時点ではレンガの壁が適用されています。

さらに、シャツの色を変更し、男性の腕の位置も変わっています。

また、OpenAI ではプロンプトに示されたコンテンツを挿入する領域にマスクを指定することで、画像を編集できます。

同じ画像を使用して、画像の背景を取り除くマスクを適用しました。

元の写真マスク

OpenAI にジャマイカのビーチに転送してほしいと指示すると、以下のような結果になりました。

家族や友人に会うときに、自分の休暇を自慢できるようになりました 😊

Image

エンドポイント: POST https://api.openai.com/v1/images/variations

既存の画像を変更した画像を作成できます。 変更方法を指定するプロンプトは不要であるため、この画像の解釈方法については AI のセンスを信用する必要があります。 また、結果のサイズと、リンクまたは Base64 のコンテンツのどちらの戻し方を希望するかを定義できます。

入力パラメーターは以下のようになります。

  • image: 必須
  • 変換する画像ファイルを指定します。
  • n: オプション。 デフォルトは 1 です。
  • このエリアでは、生成する画像の最大数を指定します。 (1~10 の数値を使用します)。
  • size: オプション。 デフォルトは 1024x1024 です。
  • このパラメーターは生成される画像のサイズを特徴づけます。 値は “256x256”、“512x512”、または “1024x1024” です。
  • response_format: オプション。 デフォルトで “url” になります。
  • この要素は、生成された画像を戻す際の形式を指定します。 値は “url” または “b64_json” です。

エンドポイント: POST https://api.openai.com/v1/images/edits

マスク ファイルに基づいて既存の画像を変更し、プロンプトに従って画像を作成できます。 また、結果の寸法と、リンクまたは Base64 のコンテンツのどちらの戻し方を希望するかを指定できます。

入力パラメーターは以下のようになります。

  • image: 必須
  • 変更する画像ファイルを指定します。
  • mask: 必須
  • このパートでは、適用するマスク画像ファイルを指定します。
  • n: オプション。 デフォルトは 1 です。
  • このエリアでは、生成する画像の最大数を指定します。 (1~10 の数値を使用します)。
  • size: オプション。 デフォルトは 1024x1024 です。
  • このパラメーターは生成される画像のサイズを特徴づけます。 値は “256x256”、“512x512”、または “1024x1024” です。
  • response_format: オプション。 デフォルトで “url” になります。
  • この要素は、生成された画像を戻す際の形式を指定します。 値は “url” または “b64_json” です。

音声ファイル

OpenAI が管理するのは画像だけではありません。 音声ファイルを使用して、提供されたレコーディングのトランスクリプションまたは翻訳を取得することもできます。

このメソッドは、固有名詞、ブランド、およびスラングを区別して正しいトランス来居プションと翻訳を提供できる Whisper モデルを使用します。 たとえば、「micromachine」というブランドについて言及するのと、一般名詞の「micro machines」をスペイン語に翻訳するのは同じことではありません。

次の例は、80 年代の有名な広告のトランスクリプションです。

<iframe allowfullscreen="" frameborder="0" height="360" src="https://www.youtube.com/embed/zLP6oT3uqV8" width="640"></iframe>

Whisper にこの音声のトランスクリプションを作成する命令の結果は以下のようになります。

{
    "text": "This is the Micromachine Man presenting the most midget miniature motorcade of micromachines. 
Each one has dramatic details, terrific trim, precision paint jobs, plus incredible micromachine pocket playsets. 
There's a police station, fire station, restaurant, service station, and more. Perfect pocket portables to take anyplace. 
And there are many miniature playsets to play with and each one comes with its own special edition micromachine vehicle and 
fun fantastic features that miraculously move. Raise the boat lift at the airport, marina, man the gun turret at 
the army base, clean your car at the car wash, raise the toll bridge. And these playsets fit together to form a micromachine world.
Micromachine pocket playsets, so tremendously tiny, so perfectly precise, so dazzlingly detailed, you'll want to pocket them all.
Micromachines and micromachine pocket playsets sold separately from Galoob. The smaller they are, the better they are."
}

素晴らしいです! そう思いませんか?

上記の結果は、Whisper モデルが受けたトレーニングによって実現しています。 これに関する情報は、OpeanAI ページに記載されている以下のダイアグラムでご覧いただけます。

 

詳細は、https://openai.com/research/whisper をご覧ください。

サービスは処理するファイルのタイプ(WAV、MP3、OGG など)を知っておく必要があるため、ファイル名に関する情報をプログラムに提供することが重要であることに注意してください。

呼び出しには Base64 コンテンツのみを含めるため、ランダムなテキストと指定拡張子でファイル名を作成できるように、ファイル拡張子も指定する必要があります。

たとえば、St.OpenAi.Msg.Audio.AudioRequest メッセージには、音声の種類を示す MP3、OGG、WAV、FLAC などの “type” プロパティがあります。

エンドポイント: https://api.openai.com/v1/audio/transcriptions

このメソッドでは、音声のコンテンツを音声の言語に文字起こしできます。

入力パラメーターは以下のようになります。

  • file: 必須
  • 文字起こしする音声ファイル(ファイル名ではありません)を指定します。 次の形式がサポートされています: FLAC、MP3、MP4、MPEG、MPGA、M4A、OGG、WAV、または WEBM
  • model: 必須。
  • トランスクリプションを作成するのに使用するモデル。 現時点では、“whisper-1” のみを使用できます。
  • language: オプション。 デフォルトでは、音声の言語です。
  • ISO-639-1 によると、指定されている場合、精度とレイテンシーが改善されます。
  • prompt: オプション。
  • これは、モデルのスタイルをガイドするため、または前の音声セグメントを継続するためのオプションのテキストです。 このメッセージは音声の言語に一致している必要があります。
  • response_format。 オプション。 デフォルトで “json” になります。
  • このパートでは、トランスクリプション出力のフォーマットを明確にします。 “json”、“text”、“verbose_json” のいずれかのオプションを使用します。
  • temperature: オプション。 デフォルト値は 0 です。
  • サンプリング温度は 0~1 です。 0.8 のように値が高くなるほど出力がよりランダムになり、0.2 のように低くなるほど集中的で確定的になります。 0 に設定すると、モデルは対数尤度を使用して、特定のしきい値に達するまで温度を自動的に高めます。

このメソッドのドキュメントは https://platform.openai.com/docs/api-reference/audio/createTranscription<をご覧ください。

エンドポイント: https://api.openai.com/v1/audio/translations

このメソッドでは、音声のコンテンツを英語に翻訳できます。

入力パラメーターは以下のようになります。

  • file: 必須
  • 翻訳する音声ファイルです(ファイル名ではありません)。 次の形式がサポートされています: FLAC、MP3、MP4、MPEG、MPGA、M4A、OGG、WAV、または WEBM
  • model: 必須。
  • このフィールドには、トランスクリプションを作成するために使用するモデルを入力します。 現時点では、“whisper-1” のみを使用できます。
  • prompt: オプション。
  • これは、モデルのスタイルをガイドするため、または前の音声セグメントを継続するためのオプションのテキストです。 このメッセージは英語である必要があります。
  • response_format。 オプション。 デフォルトで “json” になります。
  • ここでは、トランスクリプションの出力の形式を “json”、“text”、“verbose_json” から指定します。
  • temperature: オプション。 デフォルト値は 0 です。
  • サンプリング温度は 0~1 です。 0.8 のように値が高くなるほど出力がよりランダムになり、0.2 のように低くなるほど集中的で確定的になります。 0 に設定すると、モデルは対数尤度を使用して、特定のしきい値に達するまで温度を自動的に高めます。

このメソッドのドキュメントは https://platform.openai.com/docs/api-reference/audio/createTranscriptionをご覧ください。

次の内容

OpenAI は継続的に進化しているため、次回の記事ではテキストから音声への変換方法とその他の新機能をいくつか紹介します。

記事を気に入っていただけたなら、「いいね」を押してください。

0
0 131
記事 Minoru Horita · 4月 4, 2024 6m read

みなさんこんにちは! 今回は、IRIS 2024.1で実験的機能として実装されたVector Search (ベクトル検索)について紹介します。ベクトル検索は、先日リリースされたIRIS 2024.1の早期アクセスプログラム(EAP)で使用できます。IRIS 2024.1については、こちらの記事をご覧ください。

ベクトル検索でどんなことができるの?

ChatGPTをきっかけに、大規模言語モデル(LLM)や生成AIに興味を持たれている方が増えていると思います。開発者の方々の中には、中はどうなっているのか気になっている方も多いのではないでしょうか。実は、LLMや生成AIの仕組みを理解したいと思えば、ベクトルの理解は不可欠な要素となります。

ベクトルとは?

ベクトルは、高校の数学で習う「あの」ベクトルのことです。が、今回は、複数の数値をまとめて扱うデータ型であるという理解で十分です。例えば、

( 1.2, -4.5 )

という感じです。この例は、1.2と-4.5という2つの数値をまとめており、数値の個数(ここでは2)のことを次元数と言います。我々の生きている場所を3次元空間と呼ぶことがありますが、これは、3つの数値で場所が特定できることを表しています(例えば、緯度、経度、標高の3つで地球上の位置を完全に特定できます)。

ベクトルをどのように使うのか?

1
1 713
記事 Toshihiko Minamoto · 4月 4, 2024 10m read

 

皆さんもご存知のように、人工知能の世界はもう生活の中に存在しており、誰もが利用従っています。

多数のプラットフォームが、無料、サブスクリプション、または非公開の形式で、人工知能サービスを提供していますが、 コンピューティングの世界で「話題」となったことから、特に注目されているサービスは OpenAI です。最も有名な ChatGPT および DALL-E が主な原因と言えます。

<--break->OpenAI とは?

OpenAI は、人類全体にメリットのあるフレンドリーな人工知能の促進と開発を目指して、Sam Altman、Ilya Sutskever、Greg Brockman、Wojciech Zaremba、Elon Musk、John Schulman、および Andrej Karpathy によって 2015 年に設立された非営利の AI 研究所です。

設立以来、適切な目的のために使用されれば非常に強力なツールとなりうる素晴らしい製品をいくつかリリースしてきました。 とは言え、ほかのどの新しいテクノロジーと同様に、犯罪や悪行に使用される可能性があるという脅威があります。

そこで、ChatGPT サービスをテストして人工知能の定義が何かを尋ねてみました。 受け取った回答は、インターネット上で見つかった概念を蓄積して人間が回答するような方法で要約したものでした。

つまり、AI はそれをトレーニングするために使用された情報を使ってでしか返答できないということです。 内部アルゴリズムとトレーニング中に流入したデータを使用することで、記事、詩、さらにはコンピューターのコードまで作成することができます。

人工知能は、業界に大々的な影響を与え、最終的にはすべてに革命を起こすことでしょう…。 おそらく、人工知能が私たちの未来にどのように影響するのかという期待は誇示されており、公共の利益のために、それを正しく使用し始めなければなりません。

この新しいテクノロジーがすべてを変え、ChatGPT がその兄弟の GPT-4 のように私たちの世界を逆さにするという話に聞き飽きています。 どのツールも人間から仕事を奪うこともなければ、(スカイネットのように)世界を支配するようなこともありません。 ここで分析しようとしているのは、トレンドです。 これまでに達成したことを理解するために、以前の状況を振り返ることから始め、それによって自分たちの未来の状況を予測しています。

2020 年、心理学者であり認知科学者でもある Gary Marcus が GPT-2 がどのように機能したかを分析した記事を公開しました。 その動作に関する系統的な研究を実施し、そのタイプのツールが実際に何を書いているのか、どのような命令を受け取っているのかを理解できていないことを明らかにしました。

「問題はこれです。慎重に検査すると、システムはそれ自体が話していることについて見当がついていないことが明らかになります。単純なイベントの順序をたどることもできなければ、次に何が起きる可能性があるかについて確実な見当も得られないのです。」

以下のリンクで、記事全体をご覧いただけます: https://thegradient.pub/gpt2-and-the-nature-of-intelligence/

ここで、明白に進化をみることができます! GPT-3(2020 年)は、達成したいことを示した十分な入力を使ってトレーニングする必要がありましたが、現行の GPT-4 バージョンでは自然言語を使用することができるため、入力をより簡単に提供することができます。 これで、命令の意図を理解するだけでなく、それ自体が話している内容を理解している「ように見えます」。

現在は、Gary Marcus が 2020 年に GPT-2 用に作成した同じ例を使用した場合、期待どおりの結果を得られます。

OpenAI は現在、驚くほどの速さで大幅に進化した一連のツールを提供しており、適切に組み合わせれば、以前よりも効率的な結果をはるかに簡単に得ることができます。

OpenAI が提供する製品

最もよく知られた 2 つの製品について話したいと思います。DALL-E と ChatGPT です。 ただし、OpenAI には他にも、音声をテキストに書き起こし、別の言語への翻訳も行える Whisper や、検索、推奨、グループ化などの操作においてテキスト文字列の関係を測定できる Embeddings などのサービスも提供しています。

これらのサービスを使用するために必要なもの

OpenAI アカウントの作成が必要です。作成方法は非常に簡単であり、作成すれば、直接 OpenAI のウェブサイトを通じてサービスを使用できるようになります。

Chat: https://chat.openai.com

DALL-E: https://labs.openai.com

これらのサービスを IRIS から統合するので、その API を使用してアクセスする必要があります。 まず、アカウントを作成し、API を使用できるように支払方法を指定する必要があります。 費用は比較的低いですが、使用内容によってことなります。 トークンを多く消費するほど、費用が高くなります 😉

トークンとは?

モデルがテキストを理解して処理するために使用する手段です。 トークンは語または単なる文字の断片です。 たとえば、「hamburger」という語は、「ham」、「bur」、「ger」というタイルに分離されるのに対し、「pear」のような短い語は 1 つのタイルになります。 多くのトークンは「 hello」や「 bye」のように空のスペースで始まります。

API の使用は複雑ですか?

まったく複雑ではありません。 以下の手順に従えば、問題はありません。

ステップ 1: API キーの作成

ユーザーのメニューにある「View API Key」オプションを選択します。

 

ステップ 2: 新しいシークレットキーの作成

API Keys のセクションの下にあるボタンを押します。

 

非常に重要: シークレットキーが作成されると、どのような場合でも復元はできないため、安全な場所に情報を保存してください。

ステップ 3: 組織の名前の定義

組織の定義は必須ではありませんが、推奨されます。 API 呼び出しのヘッダーの一部となります。 また、組織のコードを後で使用できるようにコピーしておく必要もあります。

 これは、必要に応じて何度でも変更できます。

ステップ 4: シークレットキーと組織 ID による API 呼び出しの準備

API 呼び出しの一部として、ベアラートークン認証ヘッダーを使用し、シークレットキーを指定する必要があります。

組織 ID の横にヘッダーパラメーターとして指定されていることも必要です。

ヘッダーパラメーター
api-keysk-MyPersonalSecretKey12345
OpenAI-Organizationorg-123ABCFakeId

以下は呼び出しの例です。

POST https://api.openai.com/v1/images/create
header 'Authorization: Bearer sk-MyPersonalSecretKey12345'
header 'api-key: sk-MyPersonalSecretKey12345'
header 'OpenAI-Organization: org-123ABCFakeId '
header 'Content-Type: application/json'

この構成はすべてのエンドポイントに共通であるため、最もよく知られたメソッドがどのように機能するか見てみましょう。

Models

エンドポイント: GET https://api.openai.com/v1/models

OpenAI が使用できるように定義したすべてのモデルをダウンロードできます。 ただし、モデルごとに特徴が異なります。 Chat で使用する最新のモデルは「gpt-4」です。 すべてのモデルの ID は小文字表記であることに注意してください。

モデル名が指定されていない場合は、既存のモデルがすべて返されます。

機能と使用場所は、OpenAI ドキュメントページ(https://platform.openai.com/docs/models/overview)をご覧ください。

Chat

エンドポイント: POST https://api.openai.com/v1/chat/completions

指定されたモデルを使って、または通知を介して、会話を作成できます。 最大いくつのトークンを使用するのか、会話をいつ止めるのかを指定できます。

入力パラメーターは以下のようになります。

  • model: 必須。 これは、使用するモデルの ID です。 ListModels API を使用すると、使用できるモデルをすべて確認したり、モデルの概要で説明を確認したりできます。
  • messages: 必須。 どの役割を使用するかを示したメッセージのタイプを含みます。 ユーザーであるかアシスタントであるかを指定して、ダイアログフォームを定義できます。
    • role: メッセージの人のロールです。
    • content: メッセージのコンテンツです。
  • temperature: オプション。 示される温度を 0~2 の値で示します。 数値が高いほど、よりランダムな結果になります。 数値が低いほど、回答はより集中的で確定的になります。 定義されていない場合、デフォルト値は 1 です。
  • stop: オプション。 API がトークンを生成しなくなるシーケンスに関連しています。 「none」が指定されている場合、トークンは無限に生成されます。
  • max_tokens: オプション。 コンテンツを生成するトークンの最大数を指定し、モデルが許可する最大トークン数に制限されます。

このメソッドを説明するドキュメントは、以下のリンクをご覧ください: https://platform.openai.com/docs/api-reference/chat/create

Image

エンドポイント: POST https://api.openai.com/v1/images/generations

プロンプトパラメーターで指定されたとおりに画像を作成できます。 また、結果のサイズと、リンクまたは Base64 のコンテンツのどちらの戻し方を希望するかを定義できます。

入力パラメーターは以下のようになります。

  • message: 必須。 生成する画像を説明するテキストに関連しています。
  • n: オプション。 生成する画像の最大数に関連しています。 これは 1~10 の値です。 指定されていない場合、デフォルト値は 1 です。
  • size: オプション。 生成される画像のサイズに関連しています。 値は "256x256"、"512x512"、または "1024x1024" です。 指定されていない場合、デフォルト値は "1024x1024" です。
  • response_format: オプション。 生成された画像を戻す際の形式に関連しています。 値は "url" または "b64_json" です。 指定されていない場合、デフォルト値は "url" です。

このメソッドを説明するドキュメントは、以下のリンクをご覧ください: https://platform.openai.com/docs/api-reference/images/create

iris-openai とは?

リンク: https://openexchange.intersystems.com/package/iris-openai

このフレームワークは、必要なプロパティを含むリクエストおよびレスポンスメッセージを使用して OpenAI に接続し、Chat、Models、Images などのメソッドを使用できるように設計されています。

本番環境は、メッセージングクラスを使用して、OpenAI API に接続するビジネスオペレーションを呼び出せるように構成できます。

前述のとおり、シークレットキーと組織 ID の値を指定して本番環境を構成する必要があることに注意してください。

 

画像を作成する場合、クラスインスタンス "St.OpenAi.Msg.Images.ImagesRequest" を生成して、新しい写真を作成するためのオプションの値を入力する必要があります。

例:

Set image=##class(St.OpenAi.Msg.Images.ImagesRequest).%New()
Set image.Operation = "generations"Set image.Prompt = "Two cats with a hat reading a comic"Set image.NumOfImages = 2

完了したら、"St.OpenAi.BO.Api.Connect" ビジネスオペレーションを呼び出します。

 

注意: この場合、作成された 2 つの画像のリンクが取得されます。

{
    "created": 1683482604,
    "data": [
        {
            "url": "https://link_of_image_01.png”
        },
        {
            "url": "https://link_of_image_02.png”
        }
    ]
}

リンクではなく Base64 を操作すると指定されている場合、以下のメッセージが取得されます。

{
    "created": 1683482604,
    "data": [
        {
            "b64_json": "iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAIAAADwf7zUAAAAaGVYSWZNTQAqAAAACAACknwAAgAAACkAAAAmkoYAAgAAABgAAABQAAAAAE9wZW5BSS0tZjM5NTgwMmMzNTZiZjNkMDFjMzczOGM2OTAxYWJiNzUAAE1hZGUgd2l0aCBPcGVuQUkgREFMTC1FAAjcEcwAAQAASURBVHgBAAuE9HsBs74g/wHtAAL7AAP6AP8E+/z/BQYAAQH++vz+CQcH+fn+AgMBAwQAAPr++///AwD+BgYGAAIC/fz9//3+AAL7AwEF/wL+9/j9DQ0O/vz/+ff0CQUJAQQF/f/89fj4BwcD/wEAAfv//f4BAQQDAQH9AgIA/f3+AAABAgAA/wH8Af/9AQMGAQIBAvv+/////v/+/wEA/wEAAgMA//sCBAYCAQ”
        },
        {
            "b64_json": "D99vf7BwcI/v0A/vz9/wH8CQcI+vz8AQL9/vv+CAcF+wH/AwMA9/f8BwUEAwEB9fT+BAcKBAIB//7//gX5//v8/P7+DgkO+fr6/wD8AP8B/wAC/f4CAwD+/wT+Av79BwcE/Pz7+/sBAAD+AAQE//8BAP79AgIE///+AQABAv8BAwYA+vkB/v7/AwQE//7+/Pr6BAYCBgkE/f0B/Pr6AQP+BAED/gMC/fr+AwEC/v/+//7+CQcH+fz5BAYB9vf9BgQD+/n+BwYK/wD////9/gD5AwIDAAQE+/j6BAUD//rwAC/fr6+wYEBAQAA/4B//v6+/8AAAUDB/L49woGAQMDCfr7+wMCAQMHBPvy+AQJBQD+/wEEAfr3+gIGBgP/Af3++gUFAvz9//4A/wP/AQIGBPz+/QD7/wEDAgkGCPX29wMCAP4FBwX/+23"
        }
    ]
}

次の内容

この記事の公開後、iris-openai のコンテンツが拡張され、Whisper メソッドと画像変更に使用できるようになっているでしょう。

以降の記事では、これらのメソッドの使用方法と画像を含める方法、または音声コンテンツのトランスクリプションの作成方法を説明します。

0
0 237
記事 Toshihiko Minamoto · 4月 1, 2024 5m read

大規模言語モデル(OpenAI の GPT-4 など)の発明と一般化によって、最近までは手動での処理が非現実的または不可能ですらあった大量の非構造化データを使用できる革新的なソリューションの波が押し寄せています。 データ検索(検索拡張生成に関する優れた紹介については、Don Woodlock の ML301 コースをご覧ください)、センチメント分析、完全自律型の AI エージェントなど、様々なアプリケーションが存在します。

この記事では、IRIS テーブルに挿入するレコードに自動的にキーワードを割り当てる単純なデータタグ付けアプリケーションの構築を通じて、IRIS の Embedded Python 機能を使って、Python OpenAI ライブラリに直接インターフェース接続する方法をご紹介します。 これらのキーワードをデータの検索と分類だけでなく、データ分析の目的に使用できるる単純なデータタグ付けアプリケーションを構築します。ユースケースの例として、製品の顧客レビューを使用します。

要件

  • IRIS の実行インスタンス
  • OpenAPI API キー(こちらで作成できます)
  • 構成済みの開発環境(この記事では VS Code を使用します)

Review クラス

顧客レビューのデータモデルを定義する ObjectScript クラスの作成から始めましょう。 簡潔さを維持するために、顧客の名前、製品名、レビュー本文、および生成するキーワードの 4 つの %String フィールドのみを定義します。 クラスはオブジェクトをディスクに保存できるように、%Persistent を拡張します。

Class DataTagging.Review Extends%Persistent
{
Property Name As%String(MAXLEN = 50) [ Required ];Property Product As%String(MAXLEN = 50) [ Required ];Property ReviewBody As%String(MAXLEN = 300) [ Required ];Property Keywords As%String(MAXLEN = 300) [ SqlComputed, SqlComputeOnChange = ReviewBody ];
}

ReviewBody への挿入または更新時に、Keywords プロパティが自動的に計算されるようにしたいため、これを SqlComputed とします。計算される値についての詳細は、こちらをご覧ください。

KeywordsComputation メソッド

次に、レビュー本文に基づいてキーワードを計算するために使用するメソッドを定義します。 公式の openai Python パッケージを直接操作するために、Embedded Python を使用できます。 ただし、先にそれをインストールしておく必要があります。 以下のシェルコマンドを実行しましょう。

<your-IRIS-installation-path>/bin/irispip install --target <your-IRIS-installation-path>/Mgr/python openai

OpenAI のチャット補完 API を使用して、キーワードを生成できるようになりました。

ClassMethod KeywordsComputation(cols As %Library.PropertyHelper) As %String [ Language = python ]
{
    '''
    This method is used to compute the value of the Keywords property
    by calling the OpenAI API to generate a list of keywords based on the review body.
    '''from openai import OpenAI
client = OpenAI(
    <span class="hljs-comment"># Defaults to os.environ.get("OPENAI_API_KEY")</span>
    api_key=<span class="hljs-string">"&lt;your-api-key&gt;"</span>,
)

<span class="hljs-comment"># Set the prompt; use few-shot learning to give examples of the desired output</span>
user_prompt = <span class="hljs-string">"Generate a list of keywords that summarize the content of a customer review of a product. "</span> \
            + <span class="hljs-string">"Output a JSON array of strings.\n\n"</span> \
            + <span class="hljs-string">"Excellent watch. I got the blue version and love the color. The battery life could've been better though.\n\nKeywords:\n"</span> \
            + <span class="hljs-string">"[\"Color\", \"Battery\"]\n\n"</span> \
            + <span class="hljs-string">"Ordered the shoes. The delivery was quick and the quality of the material is terrific!.\n\nKeywords:\n"</span> \
            + <span class="hljs-string">"[\"Delivery\", \"Quality\", \"Material\"]\n\n"</span> \
            + cols.getfield(<span class="hljs-string">"ReviewBody"</span>) + <span class="hljs-string">"\n\nKeywords:"</span>
<span class="hljs-comment"># Call the OpenAI API to generate the keywords</span>
chat_completion = client.chat.completions.create(
    model=<span class="hljs-string">"gpt-4"</span>,  <span class="hljs-comment"># Change this to use a different model</span>
    messages=[
        {
            <span class="hljs-string">"role"</span>: <span class="hljs-string">"user"</span>,
            <span class="hljs-string">"content"</span>: user_prompt
        }
    ],
    temperature=<span class="hljs-number">0.5</span>,  <span class="hljs-comment"># Controls how "creative" the model is</span>
    max_tokens=<span class="hljs-number">1024</span>,  <span class="hljs-comment"># Controls the maximum number of tokens to generate</span>
)

<span class="hljs-comment"># Return the array of keywords as a JSON string</span>
<span class="hljs-keyword">return</span> chat_completion.choices[<span class="hljs-number">0</span>].message.content

}

プロンプト内では、最初に「generate a list of keywords that summarize the content of a customer review of a product」で、製品の顧客レビューのコンテンツを要約するキーワードのリストを GPT-4 でどのように生成するかについての一般的な命令を指定してから、希望する出力と 2 つのサンプル入力を指定していることに注意してください。 次に cols.getfield("ReviewBody") を挿入し、「Keywords:」の語でプロンプトを終了することで、私が提示した例と同じ形式でキーワードを提供して、文を完成させるように促しています。 これは、Few-Shot プロンプティング手法の単純な例です。

この記事を簡潔にするため、キーワードを JSON 文字列で格納するようにしていますが、本番では、DynamicArray に格納するのがお勧めです。ただ、これについては各開発者にお任せします。

キーワードの生成

それでは、管理ポータルで以下の SQL クエリを使用してテーブルに行を挿入し、データタグ付けアプリケーションをテストしてみましょう。

INSERTINTO DataTagging.Review (Name, Product, ReviewBody)
VALUES ('Ivan', 'BMW 330i', 'Solid car overall. Had some engine problems but got everything fixed under the warranty.')

以下のとおり、4 つのキーワードが自動的に生成されました。 上出来です!

まとめ

まとめると、Python コードを埋め込む InterSystems IRIS の機能によって、非構造化データを処理する際に幅広い可能性を得られます。 OpenAI の力を活用して自動データタグ付けを行うのは、この強力な機能によって達成できることの一例にすぎません。 これにより、ヒューマンエラーを縮小し、全体的な効率をさらに高めることができます。

0
0 168
記事 Toshihiko Minamoto · 11月 15, 2023 9m read

前の記事 - AI による臨床文書の保管、取得、検索の単純化

この記事では、AI を使用した文字起こしと要約によってヘルスケアに変革を起こす OpenAI の高度な言語モデルの可能性を探ります。 OpenAPI の最先端 API を活用して、録音データを文字起こしし、自然言語処理アルゴリズムを使って簡潔な要約を生成するための重要なインサイトを抽出するプロセスを掘り下げていきます。

似たような機能は Amazon Medical Transcibe や Medvoice などの既存のソリューションでも提供されていますが、この記事では、OpenAI テクノロジーを使用してこれらの強力な機能を InterSystems FHIR に実装することに焦点を当てています。

Vue.js の録音データ

Vue.js アプリのボイスレコーダーは、完全にネイティブであり、Mediarecorder インターフェースを使って JavaScript で記述されています。 これは、アプリケーションを軽量に維持しながら、録音オプションを完全に制御できるようにすることを目的としています。 以下は、録音入力の開始と停止を行うスニペットです。

// オーディオストリームをチャンクとして保存する録音開始メソッドasync startRecording() {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({
          audio: true,
        });
        this.mediaRecorder = new MediaRecorder(stream);
        this.mediaRecorder.start();

        this.mediaRecorder.ondataavailable = (event) => {
          this.chunks.push(event.data);
        };
        this.isRecording = true;
      } catch (error) {
        console.error("Error starting recording:", error);
      }
}

// 停止後にブロブを作成する(そして転写メソッドを呼び出す)録画停止メソッド
stopRecording() {
      if (this.mediaRecorder) {
        this.isLoading = true;
        this.mediaRecorder.stop();
        this.mediaRecorder.onstop = async () => {
          const blob = new Blob(this.chunks, {
            type: "audio/webm;codecs=opus",
          });
          awaitthis.sendAudioToWhisper(
            new File([blob], `file${Date.now()}.m4a`)
          );

          this.getSummary(this.transcription);
        };
      }
}

文字起こしコンポーネント

OpenAI の Whisper モデルを使った音声データの文字起こしには、いくつかの基本コンポーネントが使用されます。 以下のコードスニペットは、文字起こしプロセスに関わるステップを示します。

const apiKey = process.env.OPENAI_API_KEY;

const formData = new FormData();
formData.append("file", blob);
formData.append("model", "whisper-1");
formData.append("response_format", "json");
formData.append("temperature", "0");
formData.append("language", "en");

try {
  const response = await fetch(
    "https://api.openai.com/v1/audio/transcriptions",
    {
      method: "POST",
      headers: {
        Accept: "application/json",
        Authorization: `Bearer ${apiKey}`,
      },
      body: formData,
      redirect: "follow",
    }
  );

  const data = await response.json();
  if (data.text) {
    this.transcription = data.text;
  }
} catch (error) {
  console.error("Error sending audio to Whisper API:", error);
}

returnthis.transcription;
  1. API キー - OPENAI_API_KEY は、OpenAI API にアクセスするために必要な認証トークンです。
  2. フォームデータ - 文字起こしされる音声ファイルは、FormData オブジェクトに追加されます。 選択されたモデル(whisper-1)、レスポンス形式(json)、体温(``)、および言語(en)などの追加パラメーターも含まれています。
  3. API リクエスト - OpenAI API エンドポイント https://api.openai.com/v1/audio/transcriptions への POST リクエストには、ヘッダーとフォームデータを含むボディを指定して、fetch メソッドで送信されています。
  4. レスポンス処理 - API からのレスポンスがキャプチャされ、文字起こしされたテキストが data オブジェクトから抽出されます。 文字起こしを変数 this.transcription に割り当てて、さらに処理するか使用することができます。

要約コンポーネント

以下のコードスニペットは、OpenAI の text-davinci-003 モデルを使用したテキスト要約プロセスに関わる基本コンポーネントを示しています。

response = openai.Completion.create(
    model="text-davinci-003",
    prompt="Summarize the following text and give title and summary in json format. \
            Sample output - {\"title\": \"some-title\", \"summary\": \"some-summary\"}.\
            Input - "
    + text,
    temperature=1,
    max_tokens=300,
    top_p=1,
    frequency_penalty=0,
    presence_penalty=1
)

return response["choices"][0]["text"].replace('\n', '')
  1. モデルの選択 - model パラメーターは text-davinci-003 に設定されており、OpenAI のテキスト補完モデルが要約に使用されていることを示します。
  2. プロンプト - モデルに提供されるプロンプトは、望ましい結果を指定しています。これは入力テキストを要約して JSON 形式でタイトルと要約を返します。 入力テキストは、プロンプトに連結して処理されます。 OpenAI を通じてレスポンス変換を処理できるところに興味深いポイントがあります。 受信側での検証のみで十分であり、将来的にはコンバーターがほとんど必要なくなる可能性があります。
  3. 生成パラメーター - 生成された要約の動作と品質を制御するために、temperaturemax_tokenstop_pfrequency_penaltypresence_penalty などのパラメーターが設定されます。
  4. API リクエストとレスポンスの処理 - API リクエストを行うために、openai.Completion.create() メソッドが呼び出されます。 レスポンスがキャプチャされ、生成された要約テキストがレスポンスオブジェクトから抽出されます。 要約テキストに含まれる改行文字(\n)は、最終結果を返す前に取り除かれます。

FHIR のドキュメント参照

OpenAI テクノロジーを使用して医師と患者の会話の文字起こしと要約を実装する文脈においては、FHIR 規格内での診療記録の保管を考慮することが重要です。 FHIR は、診療記録などの医療情報を様々な医療システムやアプリケーション間で交換するための構造化された標準アプローチです。 FHIR の DocumentReference リソースは、診療記録や関連文書を保管するための専用のセクションとして機能します。

文字起こしと要約機能を医師と患者の会話ワークフローに統合する場合、生成される文字起こしと要約は、FHIR Documents リソース内の診療記録として保管できます。 これにより、生成されたインサイトへのアクセス、取得、およびヘルスケアプロバイダーやその他の承認機関の間での共有を簡単に行えます。

{
    "resourceType": "Bundle",
    "id": "1a3a6eac-182e-11ee-9901-0242ac170002",
    "type": "searchset",
    "timestamp": "2023-07-01T16:34:36Z",
    "total": 1,
    "link": [
        {
            "relation": "self",
            "url": "http://localhost:52773/fhir/r4/Patient/1/DocumentReference"
        }
    ],
    "entry": [
        {
            "fullUrl": "http://localhost:52773/fhir/r4/DocumentReference/1921",
            "resource": {
                "resourceType": "DocumentReference",
                "author": [
                    {
                        "reference": "Practitioner/3"
                    }
                ],
                "subject": {
                    "reference": "Patient/1"
                },
                "status": "current",
                "content": [
                    {
                        "attachment": {
                            "contentType": "application/json",
                            "data": ""
                        }
                    }
                ],
                "id": "1921",
                "meta": {
                    "lastUpdated": "2023-07-01T16:34:33Z",
                    "versionId": "1"
                }
            },
            "search": {
                "mode": "match"
            }
        }
    ]
}

試してみましょう

  1. プロジェクトをクローン - 次の GitHub リンクからプロジェクトリポジトリをクローンします: https://github.com/ikram-shah/iris-fhir-transcribe-summarize-export
  2. ローカルでセットアップ - 提供された指示に従って、プロジェクトをローカルマシン上にセットアップします。 セットアップ中に問題が発生した場合は、お知らせください。
  3. 患者を選択 - プロジェクト内の提供されたサンプルリストから患者を選択します。 この患者は、文字起こしと要約に使用される医師と患者の会話と関連付けられます。
  4. 対話ページ - 患者が選択されたら、プロジェクト内の対話ページに移動します。 このページで、「Take Notes」オプションを見つけてクリックし、医師と患者の会話の文字起こしプロセスを開始します。
  5. 文字起こしの表示と編集 - 文字起こしプロセスが完了したら、生成された文字起こしを表示するオプションが表示されます。 さらに整理してわかりやすくするために、文字起こしに関連付けられたタイトルと要約を編集することもできます。
  6. FHIR DocumentReference に保存 - タイトルと要約の処理が完了し、変更を保存すると、FHIR DocumentReference 内に自動的に保管されます。 これにより、関連する診療記録がキャプチャされ、それぞれの患者の記録に確実に関連付けられます。 現時点では、このプロジェクトは文字起こしテキスト全体を保存しませんが、 完全な文字起こしの補完を含めるように変更することもできます。

デモ

アプリケーション全体のデモはこちらでご覧ください: https://youtu.be/3Th6bR4rw0w

今後の方向性

AI を活用した文字起こしと要約の応用を遠隔医療通信に拡大することには、計り知れない可能性が秘められています。 これらの機能を Zoom、Teams、Google Meet などの一般的な会議プラットフォームに統合することで、医師と患者のリモート対話を合理化できる可能性があります。 遠隔医療セッションの自動文字起こしと要約機能には、正確な文書作成や分析の強化といったメリットがあります。 ただし、データプライバシーが重大な課題として残されます。 これに対応するためには、外部サーバーにデータを送信する前に、個人を特定できる情報(PII)をフィルターまたは匿名化する対策を実装する必要があります。

今後の方向性としては、ローカルで処理するためのオンデバイス AI モデルの調査、多言語コミュニケーションのサポートの改善、プライバシー維持した手法の進歩が挙げられます。

有用な実装だと思われた方は、Grand Prix 2023 でこのアプリに投票してください。

0
1 337
記事 Toshihiko Minamoto · 10月 26, 2023 4m read

問題

あわただしい臨床環境では迅速な意思決定が重要であるため、文書保管とシステムへのアクセスが合理化されていなければいくつもの障害を生み出します。 文書の保管ソリューションは存在しますが(FHIR など)、それらの文書内で特定の患者データに有意にアクセスして効果的に検索するのは、重大な課題となる可能性があります。

動機

AI により、文書の検索が非常に強力になりました。 ChromaLangchain のようなオープンソースツールを使用して、ベクトル埋め込みを保存して使用し、生成 AI API 全体でクエリを実行することで、ドキュメント上での質疑応答がかつてないほど簡単になっています。 より献身的に取り組む組織は、既存のドキュメントにインデックスを作成し、エンタープライズ用に微調整されたバージョンの GPT を構築しています。 GPT の現状に関する Andrej Karpathy の講演では、このトピックに関する素晴らしい概要が提供されています。

このプロジェクトは、医療関係者が文書を操作するあらゆるタッチポイントにおいて発生する摩擦を緩和する試みです。 医療関係者が情報を保管し、必要な情報を難なく検索できるように、入力と処理から保管と検索まで、IRIS FHIR と AI を活用しました。

ソリューション

医療関係者が音声メモを記録できるフルスタックのウェブアプリを構築しました。 これらのメモは、Open AI を使って文字起こしされ、要約されてから FHIR サーバーに保管されます。 保管されたドキュメントは、インデックス作成されてから、セマンティック検索で使用できるようになります。  

デモ動画

主な機能

  1. ウェブアプリ - 患者、観察、遭遇に関する診療情報を表示します。 これは Vue.js で構築されています。
  2. 音声データの文字起こし - Open AI Whisper API を使って、録音を正確なテキストに文字起こしします。
  3. テキストの要約 - 文字起こしされた内容を必要なフォーマットで要約してタイトルが付けられます。 症状、診断などの具体的なセクションなどです。 これは、text-da-vinci-003 を使った Open AI テキスト補完 API で行われます。
  4. ドキュメントの保管 - 要約されたドキュメントは、DocumentReference アーティファクトを使って FHIR に保管されます。
  5. セマンティックドキュメント検索 - 保管されたドキュメントはインデックス作成されて、チャンクとして Chroma に保管されます。 これは、Langchain を使用して検索スペースを制限してセマンティック検索に GPT トークンを控えめに使用するために使用されます。 現時点では、使用できるドキュメント数が少ないため、検索時にドキュメントを読み込んでいますが、 非同期的にバックグラウンドでインデックス作成するように変更することが可能です。
  6. ドキュメントのエクスポート - 最後に、ドキュメントを Google Docs に、その他のデータを Google Sheets にエクスポートするオプションがあります。 ユーザーは他の医療関係者や患者とのコラボレーションとやり取りを簡単に行えるように、OAuth を使って特定のアカウントにログインし、ドキュメントをエクスポートすることができます。

試してみましょう

次の GitHub リンクからプロジェクトリポジトリをクローンします: https://github.com/ikram-shah/iris-fhir-transcribe-summarize-export。 提供された指示に従って、プロジェクトをローカルマシン上にセットアップしてください。 期待される動作が得られない場合は、お知らせください。

ご意見とフィードバック

現在使用できる高度言語モデルと大量のデータを合わせることで、ヘルスケア分野の特に文書管理の領域に革命を起こす大きな可能性があります。 以下に、ご意見やフィードバックをお寄せください。 このプロジェクトの背後にある技術的な情報について、さらに多くの記事を投稿する予定です。

このプロジェクトに期待できると思われる場合は、Grand Prix コンテストでこのアプリに投票してください!

0
0 184
お知らせ Mihoko Iijima · 6月 12, 2023

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

InterSystems グランプリコンテスト2023 では、InterSystems IRIS data platform を使用する機能であればどんな内容でもご応募いただけます。

以下の機能を含めた場合、ボーナスポイントを獲得できます。

詳細は以下の通りです。

  • LLM AI や LangChain の使用:Chat GPT、Bard など - 6ポイント
  • InterSystems FHIR SQL Builder- 5ポイント
  • InterSystems FHIR - 3ポイント
  • IntegratedML - 4ポイント
  • Native API - 3ポイント
  • Embedded Python - 4ポイント
  • Interoperability - 3ポイント
  • Production EXtension(PEX) - 2ポイント
  • Adaptive Analytics (AtScale) のキューブの利用 - 3ポイント
  • Tableau, PowerBI, Logi の利用 - 3ポイント
  • InterSystems IRIS BI - 3ポイント
  • Docker container の利用 - 2ポイント 
  • ZPM Package によるデプロイ - 2ポイント
  • オンラインデモ - 2ポイント
  • ユニットテスト - 2ポイント
  • InterSystems Community Idea に投稿された内容の実装 - 4ポイント
  • コミュニティ(USコミュニティ)に記事を投稿する(最初の記事) - 2ポイント
  • コミュニティ(USコミュニティ)に2つ目の記事を投稿する - 1ポイント
  • Code Qualityをパスする - 1ポイント
  • はじめてチャレンジされた方 - 3ポイント
  • YouTubeにビデオを公開- 3ポイント
0
0 107
お知らせ Mihoko Iijima · 4月 4, 2023

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

技術文書ライティングコンテストの受賞者が発表されたばかりですが、次のコンテスト:InterSystems IRIS Cloud SQL and IntegratedML コンテスト 2023 のテクノロジーボーナス詳細が決定しましたのでお知らせします📣

  • IntegratedML の利用
  • オンラインデモ
  • コミュニティに記事を投稿する
  • コミュニティに2つ目の記事を投稿する
  • YouTubeにビデオを公開する
  • はじめてチャレンジされた方
  • InterSystems Idea 内 Community Opportunityの実装

獲得ポイントについて詳細は、以下ご参照ください。

0
0 115
お知らせ Mihoko Iijima · 3月 27, 2023

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

次のプログラミングコンテストの詳細が決定し「IRIS Cloud SQLのデータを利用してAI/MLソリューションを作成する」がテーマとなりました。

🏆 InterSystems IRIS Cloud SQL and IntegratedML コンテスト 🏆

期間: 2023年4月3日~23日

賞金総額: $13,500

 

0
0 109
記事 Toshihiko Minamoto · 3月 23, 2023 10m read

腎臓病は、医学会でよく知られるいくつかのパラメーターから発見することが可能です。 この測定により、医学界とコンピューター化されたシステム(特に AI)を支援すべく、科学者である Akshay Singh は、腎臓病の検出/予測における ML アルゴリズムをトレーニングするための非常に便利なデータセットを公開しました。 このデータセットは、ML の最大級のデータリポジトリとして最もよく知られている Kaggle に公開されています。https://www.kaggle.com/datasets/akshayksingh/kidney-disease-dataset

データセットについて

腎臓病データセットには、以下のメタデータ情報が含まれています(出典: https://www.kaggle.com/datasets/akshayksingh/kidney-disease-dataset

  • 赤血球、足浮腫、血糖値などの 25 種類の特徴量を含む 400 行のデータセット。
  • 患者が慢性腎臓病を患っているかどうかを分類することが目的
  • 分類は、'classification' と名付けられた属性が 'ckd'(慢性腎臓病)であるか 'notckd' であるかに基づいて行われます。
  • データセットの作成者は、テキストと数値のマッピングやその他の変更点を含むデータセットのクリーニングを実施しました。 クリーニングの後、データセット作成者は EDA(探索的データ解析)を行い、データセットをトレーニングとテストに分割し、それらにモデルを適用しました。 分類結果は、最初はあまり満足のいくものではないことがわかっています。 そこで、Nan 値のある行をドロップする代わりに、ラムダ関数を使用して、それらの行を各カラムのモードに置き換えて、 もう一度データセットをトレーニングセットとテストセットに分割し、モデルに適用しました。 今度は、結果が改善され、ランダムフォレストと意思決定ツリーが、1.0 と 0 の誤分類の精度で最も良く実行することがわかりました。 分類のパフォーマンスは、混合行列、分類レポート、および制度の出力によって測定されています。

データセットの情報(出典: https://archive.ics.uci.edu/ml/datasets/chronic_kidney_disease )

データセットの収集には以下の表現を使用しています。

age - 年齢 bp - 血圧 sg - 比重 al - アルブミン値 su - 糖分 rbc - 赤血球 pc - 膿細胞 pcc - 膿細胞の塊 ba - 細菌 bgr - ランダム血糖値 bu - 血液尿素 sc - 血清クレアチニン sod - ナトリウム pot - カリウム hemo - ヘモグロビン pcv - ヘマトクリット値 wc - 白血球数 rc - 赤血球数 htn - 高血圧 dm - 糖尿病 cad - 冠動脈疾患 appet - 食欲 pe - 足浮腫 ane - 貧血症 class - クラス

属性の情報(出典: https://archive.ics.uci.edu/ml/datasets/chronic_kidney_disease)

24 + class = 25(数値 11 個、名義尺度 14 個)を使用します。

  1. 年齢(数値) 年数
  2. 血圧(数値) bp(mm/Hg)
  3. 比重(名義尺度) sg - (1.005,1.010,1.015,1.020,1.025)
  4. アルブミン値(名義尺度) al - (0,1,2,3,4,5)
  5. 糖分(名義尺度) su - (0,1,2,3,4,5)
  6. 赤血球(名義尺度) rbc - (normal,abnormal)
  7. 膿細胞(名義尺度) pc - (normal,abnormal)
  8. 膿細胞集塊(名義尺度) pcc - (present,notpresent)
  9. 最近(名義尺度) ba - (present,notpresent)
  10. ランダム血糖値(数値) bgr(mgs/dl)
  11. 血液尿素(数値) bu(mgs/dl)
  12. 血清クレアチニン(数値) sc(mgs/dl)
  13. ナトリウム(数値) sod(mEq/L)
  14. カリウム(数値) pot(mEq/L)
  15. ヘモグロビン(数値) hemo(gms)
  16. ヘマトクリット値(数値)
  17. 白血球数(数値) wc(cells/cumm)
  18. 赤血球数(数値) rc(100万/cmm)
  19. 高血圧(名義尺度) htn - (yes,no)
  20. 糖尿病(名義尺度) dm - (yes,no)
  21. 冠動脈疾患(名義尺度) cad - (yes,no)
  22. 食欲(名義尺度) appet - (good,poor)
  23. 足浮腫(名義尺度) pe - (yes,no)
  24. 貧血症(名義尺度) ane - (yes,no)
  25. 区分(名義尺度) class - (ckd,notckd)

Kaggle から腎臓データを取得する

Kaggle の腎臓データは、Health-Dataset アプリケーション(https://openexchange.intersystems.com/package/Health-Dataset)を使って IRIS テーブルに読み込めます。 これを行うには、module.xml プロジェクトから依存関係(Health Dataset 用の ModuleReference)を設定します。

 
Health Dataset アプリケーションリファレンスを使用した Module.xml
<?xmlversion="1.0" encoding="UTF-8"?>
<Exportgenerator="Cache"version="25">
  <Documentname="predict-diseases.ZPM">
<Module>
      <Name>predict-diseases</Name>
      <Version>1.0.0</Version>
      <Packaging>module</Packaging>
      <SourcesRoot>src/iris</SourcesRoot>
      <ResourceName="dc.predict.disease.PKG"/>
      <Dependencies>
        <ModuleReference>
          <Name>swagger-ui</Name>
          <Version>1.*.*</Version>
        </ModuleReference>
        <ModuleReference>
          <Name>dataset-health</Name>
          <Version>*</Version>
        </ModuleReference>
      </Dependencies>
       <CSPApplication
        Url="/predict-diseases"
        DispatchClass="dc.predict.disease.PredictDiseaseRESTApp"
        MatchRoles=":{$dbrole}"
        PasswordAuthEnabled="1"
        UnauthenticatedEnabled="1"
        Recurse="1"
        UseCookies="2"
        CookiePath="/predict-diseases"
       />
       <CSPApplication
        CookiePath="/disease-predictor/"
        DefaultTimeout="900"
        SourcePath="/src/csp"
        DeployPath="${cspdir}/csp/${namespace}/"
        MatchRoles=":{$dbrole}"
        PasswordAuthEnabled="0"
        Recurse="1"
        ServeFiles="1"
        ServeFilesTimeout="3600"
        UnauthenticatedEnabled="1"
        Url="/disease-predictor"
        UseSessionCookie="2"
      />
    </Module>
   
  </Document>
</Export>

腎臓病を予測するための Web フロントエンドとバックエンドのアプリケーション

Open Exchange アプリのリンク(https://openexchange.intersystems.com/package/Disease-Predictor)に移動し、以下の手順に従います。

  1. リポジトリを任意のローカルディレクトリに Clone/git pull します。
$ git clone https://github.com/yurimarx/predict-diseases.git
  1. このディレクトリで Docker ターミナルを開き、以下を実行します。
$ docker-compose build
  1. IRIS コンテナを実行します。
$ docker-compose up -d
  1. AI モデルをトレーニングするため管理ポータルのクエリ実行(http://localhost:52773/csp/sys/exp/%25CSP.UI.Portal.SQL.Home.zen?$NAMESPACE=USER)に移動します。

  2. トレーニングに使用するビューを作成します。

CREATE VIEW KidneyDiseaseTrain AS SELECT age, al, ane, appet, ba, bgr, bp, bu, cad, classification, dm, hemo, htn, pc, pcc, pcv, pe, pot, rbc, rc, sc, sg, sod, su, wc FROM dc_data_health.KidneyDisease
  1. ビューを使用して AI モデルを作成します。
CREATE MODEL KidneyDiseaseModel PREDICTING (classification) FROM KidneyDiseaseTrain
  1. モデルをトレーニングします。
TRAIN MODEL KidneyDiseaseModel
  1. http://localhost:52773/disease-predictor/index.html に移動し、Disease Predictor フロントエンドを使用して、以下のように疾患を予測します。 Kidney-Predictor

背後の処理

腎臓病を予測するためのバックエンドのクラスメソッド

InterSystems IRIS では、前に作成されたモデルを使って、SELECT の実行により予測することができます。

 
腎臓病を予測するためのバックエンドのクラスメソッド
///腎臓病の予測
ClassMethodPredictKidneyDisease()As%Status
{
    Try{
      Setdata={}.%FromJSON(%request.Content)
      Set%response.Status=200
      Set%response.Headers("Access-Control-Allow-Origin")="*"
     
      Setqry="SELECT PREDICT(KidneyDiseaseModel) As PredictedKidneyDisease, "
                  _"age, al, ane, appet, ba, bgr, bp, bu, cad, dm, "
                  _"hemo, htn, pc, pcc, pcv, pe, pot, rbc, rc, sc, sg, sod, su, wc "
                  _"FROM (SELECT "_data.age_" AS age, "
                  _data.al_" As al, "
                  _"'"_data.ane_"'"_" AS ane, "
                  _"'"_data.appet_"'"_" AS appet, "
                  _"'"_data.ba_"'"_" As ba, "
                  _data.bgr_" As bgr, "
                  _data.bp_" AS bp, "
                  _data.bu_" AS bu, "
                  _"'"_data.cad_"'"_" As cad, "
                  _"'"_data.dm_"'"_" As dm, "
                  _data.hemo_" AS hemo, "
                  _"'"_data.htn_"'"_" AS htn, "
                  _"'"_data.pc_"'"_" As pc, "
                  _"'"_data.pcc_"'"_" As pcc, "
                  _data.pcv_" AS pcv, "
                  _"'"_data.pe_"'"_" AS pe, "
                  _data.pot_" As pot, "
                  _"'"_data.rbc_"'"_" As rbc, "
                  _data.rc_" AS rc, "
                  _data.sc_" AS sc, "
                  _data.sg_" As sg, "
                  _data.sod_" As sod, "
                  _data.su_" AS su, "
                  _data.wc_" AS wc)"
      SettStatement=##class(%SQL.Statement).%New()
      SetqStatus=tStatement.%Prepare(qry)
      IfqStatus'=1{WRITE"%Prepare failed:"DO$System.Status.DisplayError(qStatus)QUIT}
      Setrset=tStatement.%Execute()
      Dorset.%Next()
 
      SetResponse={}
      SetResponse.classification=rset.PredictedKidneyDisease
      SetResponse.age=rset.age
      SetResponse.al=rset.al
      SetResponse.ane=rset.ane
      SetResponse.appet=rset.appet
      SetResponse.ba=rset.ba
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">bgr</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">bgr</span><span style="color: #d4d4d4;"> </span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">bp</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">bp</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">bu</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">bu</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">cad</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">cad</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">dm</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">dm</span><span style="color: #d4d4d4;"> </span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">hemo</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">hemo</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">htn</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">htn</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">pc</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">pc</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">pcc</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">pcc</span><span style="color: #d4d4d4;"> </span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">pcv</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">pcv</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">pe</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">pe</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">pot</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">pot</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">rbc</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">rbc</span><span style="color: #d4d4d4;"> </span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">rc</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">rc</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">sc</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">sc</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">sg</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">sg</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">sod</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">sod</span><span style="color: #d4d4d4;"> </span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">su</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">su</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">wc</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">wc</span>
          </div>   
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Write</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%ToJSON</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Return</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">1</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">}</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">Catch</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">write</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error name: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Name</span><span style="color: #ffffff;">,</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error code: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Code</span><span style="color: #ffffff;">,</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error location: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Location</span><span style="color: #ffffff;">,</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Additional data: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Data</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">!</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Return</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;"></span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">}</span>
          </div>
          
          <div>
            <span style="color: #ffffff;">}</span>
          </div>
        </div>
      </div>
    </div>

これで、どの Web アプリケーションもこの予測を使用して、結果を表示できるようになりました。 predict-diseases アプリケーションのソースコードは、frontend フォルダをご覧ください。

0
0 151
記事 Toshihiko Minamoto · 3月 10, 2023 9m read

糖尿病は、医学会でよく知られるいくつかのパラメーターから発見することが可能です。 この測定により、医学界とコンピューター化されたシステム(特に AI)を支援すべく、(米)国立糖尿病・消化器・腎疾病研究所(NIDDK)は、糖尿病の検出/予測における ML アルゴリズムをトレーニングするための非常に便利なデータセットを公開しました。 このデータセットは、ML の最大級のデータリポジトリとして最もよく知られている Kaggle に公開されています: https://www.kaggle.com/datasets/mathchi/diabetes-data-set

糖尿病データセットには、以下のメタデータ情報が含まれています(出典: https://www.kaggle.com/datasets/mathchi/diabetes-data-set):

  • Pregnancies: 妊娠回数
  • Glucose: 経口ブドウ糖負荷試験における 2 時間後の血漿グルコース濃度
  • BloodPressure: 拡張期血圧(mm Hg)
  • SkinThickness: 上腕三頭筋皮下脂肪厚(mm)
  • Insulin: 2 時間血清インスリン(mu U/ml)
  • BMI: ボディマス指数(体重 kg/(身長 m)^2)
  • DiabetesPedigreeFunction: 糖尿病血統要因(親族の糖尿病歴、およびこれらの親族と患者の遺伝的関係に関するいくつかのデータが提供されました。 この遺伝的影響の測定により、真性糖尿病の発症に伴う遺伝的リスクについての考えが得られました - 出典: https://machinelearningmastery.com/case-study-predicting-the-onset-of-diabetes-within-five-years-part-1-of-3/
  • Age: 年齢
  • Outcome: クラス変数(0 または 1)

インスタンス数: 768

属性数: 8 + class

各属性について:(すべて数値)

  1. 妊娠回数
  2. 経口ブドウ糖負荷試験における 2 時間後の血漿グルコース濃度
  3. 拡張期血圧(mm Hg)
  4. 上腕三頭筋皮下脂肪厚(mm)
  5. 2 時間血清インスリン(mu U/ml)
  6. ボディマス指数(体重 kg/(身長 m)^2)
  7. 糖尿病血統要因
  8. 年齢
  9. クラス変数(0 または 1)

属性値の欠落: あり

クラス分布:(クラス値 1 は「糖尿病の検査で陽性」として解釈)

Kaggle から糖尿病データを取得する

Kaggle の糖尿病データは、Health-Dataset アプリケーション(https://openexchange.intersystems.com/package/Health-Dataset)を使って IRIS テーブルに読み込めます。 これを行うには、module.xml プロジェクトから依存関係(Health Dataset 用の ModuleReference)を設定します。

 
Health Dataset アプリケーションリファレンスを含む Module.xml
<?xml version="1.0" encoding="UTF-8"?>
<Exportgenerator="Cache"version="25">
    <div>
      <span style="color: #d4d4d4;">  </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">Document</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">name</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"predict-diseases.ZPM"</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">    </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">Module</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">      </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">Name</span><span style="color: #808080;">&gt;</span><span style="color: #d4d4d4;">predict-diseases</span><span style="color: #808080;">&lt;/</span><span style="color: #569cd6;">Name</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">      </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">Version</span><span style="color: #808080;">&gt;</span><span style="color: #d4d4d4;">1.0.0</span><span style="color: #808080;">&lt;/</span><span style="color: #569cd6;">Version</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">      </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">Packaging</span><span style="color: #808080;">&gt;</span><span style="color: #d4d4d4;">module</span><span style="color: #808080;">&lt;/</span><span style="color: #569cd6;">Packaging</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">      </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">SourcesRoot</span><span style="color: #808080;">&gt;</span><span style="color: #d4d4d4;">src/iris</span><span style="color: #808080;">&lt;/</span><span style="color: #569cd6;">SourcesRoot</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">      </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">Resource</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">Name</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"dc.predict.disease.PKG"</span><span style="color: #808080;">/&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">      </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">Dependencies</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">ModuleReference</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">          </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">Name</span><span style="color: #808080;">&gt;</span><span style="color: #d4d4d4;">swagger-ui</span><span style="color: #808080;">&lt;/</span><span style="color: #569cd6;">Name</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">          </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">Version</span><span style="color: #808080;">&gt;</span><span style="color: #d4d4d4;">1.*.*</span><span style="color: #808080;">&lt;/</span><span style="color: #569cd6;">Version</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #808080;">&lt;/</span><span style="color: #569cd6;">ModuleReference</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">ModuleReference</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">          </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">Name</span><span style="color: #808080;">&gt;</span><span style="color: #d4d4d4;">dataset-health</span><span style="color: #808080;">&lt;/</span><span style="color: #569cd6;">Name</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">          </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">Version</span><span style="color: #808080;">&gt;</span><span style="color: #d4d4d4;">*</span><span style="color: #808080;">&lt;/</span><span style="color: #569cd6;">Version</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #808080;">&lt;/</span><span style="color: #569cd6;">ModuleReference</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">      </span><span style="color: #808080;">&lt;/</span><span style="color: #569cd6;">Dependencies</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">       </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">CSPApplication</span><span style="color: #d4d4d4;"> </span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">Url</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"/predict-diseases"</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">DispatchClass</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"dc.predict.disease.PredictDiseaseRESTApp"</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">MatchRoles</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">":{$dbrole}"</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">PasswordAuthEnabled</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"1"</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">UnauthenticatedEnabled</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"1"</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">Recurse</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"1"</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">UseCookies</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"2"</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">CookiePath</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"/predict-diseases"</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">       </span><span style="color: #808080;">/></span>
    </div>

    <div>
      <span style="color: #d4d4d4;">       </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">CSPApplication</span><span style="color: #d4d4d4;"> </span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">CookiePath</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"/disease-predictor/"</span><span style="color: #d4d4d4;"> </span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">DefaultTimeout</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"900"</span><span style="color: #d4d4d4;"> </span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">SourcePath</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"/src/csp"</span><span style="color: #d4d4d4;"> </span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">DeployPath</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"${cspdir}/csp/${namespace}/"</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">MatchRoles</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">":{$dbrole}"</span><span style="color: #d4d4d4;"> </span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">PasswordAuthEnabled</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"0"</span><span style="color: #d4d4d4;"> </span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">Recurse</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"1"</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">ServeFiles</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"1"</span><span style="color: #d4d4d4;"> </span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">ServeFilesTimeout</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"3600"</span><span style="color: #d4d4d4;"> </span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">UnauthenticatedEnabled</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"1"</span><span style="color: #d4d4d4;"> </span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">Url</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"/disease-predictor"</span><span style="color: #d4d4d4;"> </span>
    </div>

    <div>
      <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">UseSessionCookie</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"2"</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">      </span><span style="color: #808080;">/></span>
    </div>

    <div>
      <span style="color: #d4d4d4;">    </span><span style="color: #808080;">&lt;/</span><span style="color: #569cd6;">Module</span><span style="color: #808080;">&gt;</span>
    </div>

    <div>
      <span style="color: #d4d4d4;">    </span>
    </div>

    <div>
      <span style="color: #d4d4d4;">  </span><span style="color: #808080;">&lt;/</span><span style="color: #569cd6;">Document</span><span style="color: #808080;">&gt;</span>
    </div>
</Export>

糖尿病を予測するための Web フロントエンドとバックエンドのアプリケーション

Open Exchange アプリのリンク(https://openexchange.intersystems.com/package/Disease-Predictor)に移動し、以下の手順に従います。

  1. リポジトリを任意のローカルディレクトリに Git pull します。
$ git clone https://github.com/yurimarx/predict-diseases.git
  1. このディレクトリで Docker ターミナルを開き、以下を実行します。
$ docker-compose build
  1. IRIS コンテナを実行します。
$ docker-compose up -d 
  1. AI モデルをトレーニングするための Execute Query into Management Portal(http://localhost:52773/csp/sys/exp/%25CSP.UI.Portal.SQL.Home.zen?$NAMESPACE=USER)に移動します。
  2. トレーニングに使用するビューを作成します。
CREATE VIEW DiabetesTrain AS SELECT Outcome, age, bloodpressure, bmi, diabetespedigree, glucose, insulin, pregnancies, skinthickness FROM dc_data_health.Diabetes
  1. ビューを使用して AI モデルを作成します。
CREATE MODEL DiabetesModel PREDICTING (Outcome) FROM DiabetesTrain
  1. モデルをトレーニングします。
TRAIN MODEL DiabetesModel
  1. http://localhost:52773/disease-predictor/index.html に移動し、Disease Predictor フロントエンドを使用して、以下のように疾患を予測します。 Disease-Predictor

背後の処理

糖尿病を予測するためのバックエンドのクラスメソッド

InterSystems IRIS では、前に作成されたモデルを使って予測するSELECT を実行することができます。

 
糖尿病を予測するためのバックエンドのクラスメソッド
///Predict Diabetes
ClassMethodPredictDiabetes()As%Status
          <div>
            <span style="color: #ffffff;">{</span>
          </div>
          <div>

    Try{

          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span><span style="color: #ffffff;">}</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%FromJSON</span><span style="color: #ffffff;">(</span><span style="color: #64c9ff;">%request</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Content</span><span style="color: #ffffff;">)</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">qry</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"SELECT PREDICT(DiabetesModel) As PredictedDiabetes, "</span>
          </div>
          <div>
            <span style="color: #d4d4d4;">                  </span><span style="color: #ffffff;">_</span><span style="color: #d4b57c;">"age, bloodpressure, bmi, diabetespedigree, glucose, insulin, "</span>
          </div>
          <div>
            <span style="color: #d4d4d4;">                  </span><span style="color: #ffffff;">_</span><span style="color: #d4b57c;">"pregnancies, skinthickness "</span><span style="color: #d4d4d4;"> </span>
          </div>
          <div>
            <span style="color: #d4d4d4;">                  </span><span style="color: #ffffff;">_</span><span style="color: #d4b57c;">"FROM (SELECT "</span><span style="color: #ffffff;">_</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">age</span><span style="color: #ffffff;">_</span><span style="color: #d4b57c;">" AS age, "</span>
          </div>
          <div>
            <span style="color: #d4d4d4;">                  </span><span style="color: #ffffff;">_</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">bloodpressure</span><span style="color: #ffffff;">_</span><span style="color: #d4b57c;">" As bloodpressure, "</span><span style="color: #d4d4d4;"> </span>
          </div>
          <div>
            <span style="color: #d4d4d4;">                  </span><span style="color: #ffffff;">_</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">bmi</span><span style="color: #ffffff;">_</span><span style="color: #d4b57c;">" AS bmi, "</span>
          </div>
          <div>
            <span style="color: #d4d4d4;">                  </span><span style="color: #ffffff;">_</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">diabetespedigree</span><span style="color: #ffffff;">_</span><span style="color: #d4b57c;">" AS diabetespedigree, "</span>
          </div>
          <div>
            <span style="color: #d4d4d4;">                  </span><span style="color: #ffffff;">_</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">glucose</span><span style="color: #ffffff;">_</span><span style="color: #d4b57c;">" As glucose, "</span>
          </div>
          <div>
            <span style="color: #d4d4d4;">                  </span><span style="color: #ffffff;">_</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">insulin</span><span style="color: #ffffff;">_</span><span style="color: #d4b57c;">" AS insulin, "</span>
          </div>
          <div>
            <span style="color: #d4d4d4;">                  </span><span style="color: #ffffff;">_</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">pregnancies</span><span style="color: #ffffff;">_</span><span style="color: #d4b57c;">" As pregnancies, "</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">                  </span><span style="color: #ffffff;">_</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">skinthickness</span><span style="color: #ffffff;">_</span><span style="color: #d4b57c;">" AS skinthickness)"</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">tStatement</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">##class</span><span style="color: #ffffff;">(</span><span style="color: #4ec9b0;">%SQL</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">Statement</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%New</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">qStatus</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">tStatement</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Prepare</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">qry</span><span style="color: #ffffff;">)</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">If</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">qStatus</span><span style="color: #ffffff;">'=</span><span style="color: #d4b57c;">1</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span><span style="color: #ffffff;">WRITE</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"%Prepare failed:"</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">DO</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">$System</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">Status</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">DisplayError</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">qStatus</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">QUIT</span><span style="color: #ffffff;">}</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">tStatement</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Execute</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Do</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Next</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
          </div>   
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span><span style="color: #ffffff;">}</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">PredictedDiabetes</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">PredictedDiabetes</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">age</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">age</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">bloodpressure</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">bloodpressure</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">bmi</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">bmi</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">diabetespedigree</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">diabetespedigree</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">glucose</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">glucose</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">insulin</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">insulin</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">pregnancies</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">pregnancies</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">skinthickness</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">rset</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">skinthickness</span>
          </div>   
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">%response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Status</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">200</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">%response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Headers</span><span style="color: #ffffff;">(</span><span style="color: #d4b57c;">"Access-Control-Allow-Origin"</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">=</span><span style="color: #d4b57c;">"*"</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Write</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%ToJSON</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Return</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">1</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">}</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">Catch</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">write</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error name: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Name</span><span style="color: #ffffff;">,</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error code: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Code</span><span style="color: #ffffff;">,</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error location: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Location</span><span style="color: #ffffff;">,</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Additional data: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Data</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">!</span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Return</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;"></span>
          </div>
          
          <div>
            <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">}</span>
          </div>
          
          <div>
            <span style="color: #ffffff;">}</span>
          </div>
        </div>
      </div>
    </div>

これで、どの Web アプリケーションもこの予測を使用して、結果を表示できるようになりました。 predict-diseases アプリケーションのソースコードは、frontend フォルダをご覧ください。

0
0 399
記事 Toshihiko Minamoto · 12月 16, 2022 6m read

母体リスクは、医学界でよく知られているいくつかのパラメーターから測定できます。 この測定により、医学界とコンピューター化されたシステム(特に AI)を支援すべく、科学者である Yasir Hussein Shakir は、母体リスクの検出/予測における ML アルゴリズムをトレーニングするための非常に便利なデータセットを公開しました。 このデータセットは、ML の最大級のデータリポジトリとして最もよく知られている Kaggle に公開されています。

https://www.kaggle.com/code/yasserhessein/classification-maternal-health-5-algorithms-ml

データセットについて

妊娠中と出産後の母体のヘルスケアに関する情報の不足により、妊娠中の女性の多くは、妊娠に関わる問題で死亡しています。 これは、農村地域や新興国の下位中流家庭の間でより一般的に起きている問題です。 妊娠中は、状態を絶えず観察することで、胎児の適切な成長と安全な出産を保証する必要があります(出典: https://www.kaggle.com/code/yasserhessein/classification-maternal-health-5-algorithms-ml)。

データは、IoT ベースのリスク監視システムを通じて、様々な病院、地域の診療所、妊産婦ヘルスケアから収集されています。

  • Age: 女性が妊娠したときの年齢
  • SystolicBP: 最高血圧(mmHg)。妊娠中に重要な属性の 1 つ。
  • DiastolicBP: 最低血圧(mmHg)。妊娠中に重要な属性の 1 つ。
  • BS: モル濃度(mmol/L)による血糖値。
  • HeartRate: 1 分あたりの通常の安静時心拍数。
  • Risk Level: 前の属性を考慮した妊娠中の予測リスク強度レベル。

Kaggle から母体リスクデータを取得する

Kaggle の母体リスクデータは、Health-Dataset アプリケーション(https://openexchange.intersystems.com/package/Health-Dataset)を使って IRIS テーブルに読み込めます。 これを行うには、module.xml プロジェクトから依存関係(Health Dataset 用の ModuleReference)を設定します。

 
Health Dataset アプリケーションリファレンスを含む Module.xml
<?xml version="1.0" encoding="UTF-8"?>
<Exportgenerator="Cache"version="25">
  <Documentname="predict-diseases.ZPM">
    <Module>
      <Name>predict-diseases</Name>
      <Version>1.0.0</Version>
      <Packaging>module</Packaging>
      <SourcesRoot<src/iris</SourcesRoot>
      <ResourceName="dc.predict.disease.PKG"/>
      <Dependencies>
        <ModuleReference>
          <Name>swagger-ui</Name>
          <Version>1.*.*</Version>
        </ModuleReference>
        <ModuleReference>
          <Name>dataset-health</Name>
          <Version>*</Version>
        </ModuleReference>
      </Dependencies>
       <CSPApplication
        Url="/predict-diseases"
        DispatchClass="dc.predict.disease.PredictDiseaseRESTApp"
        MatchRoles=":{$dbrole}"
        PasswordAuthEnabled="1"
        UnauthenticatedEnabled="1"
        Recurse="1"
        UseCookies="2"
        CookiePath="/predict-diseases"
       />
       <CSPApplication
        CookiePath="/disease-predictor/"
        DefaultTimeout="900"
        SourcePath="/src/csp"
        DeployPath="${cspdir}/csp/${namespace}/"
        MatchRoles=":{$dbrole}"
        PasswordAuthEnabled="0"
        Recurse="1"
        ServeFiles="1"
        ServeFilesTimeout="3600"
        UnauthenticatedEnabled="1"
        Url="/disease-predictor"
        UseSessionCookie="2"
      />
    </Module>
   
  </Document>
</Export>

母体リスクを予測するための Web フロントエンドとバックエンドのアプリケーション

Open Exchange アプリのリンク(https://openexchange.intersystems.com/package/Disease-Predictor)に移動し、以下の手順に従います。

  • リポジトリを任意のローカルディレクトリに Clone/git pull します。
$ git clone https://github.com/yurimarx/predict-diseases.git
  • このディレクトリで Docker ターミナルを開き、以下を実行します。
$ docker-compose build
  • IRIS コンテナを実行します。
$ docker-compose up -d 
CREATE VIEW MaternalRiskTrain AS SELECT BS, BodyTemp, DiastolicBP, HeartRate, RiskLevel, SystolicBP, age FROM dc_data_health.MaternalHealthRisk
  • ビューを使用して AI モデルを作成します。
CREATE MODEL MaternalRiskModel PREDICTING (RiskLevel) FROM MaternalRiskTrain 
  • モデルをトレーニングします。
TRAIN MODEL MaternalRiskModel

背後の処理

母体リスク疾患を予測するためのバックエンドのクラスメソッド

InterSystems IRIS では、前に作成されたモデルを使って、SELECT の実行により予測することができます。

 
母体リスク疾患を予測するためのバックエンドのクラスメソッド
///母体リスクの予測
ClassMethodPredictMaternalRisk()As%Status
{
    Try{
      Setdata={}.%FromJSON(%request.Content)
      Set%response.Status=200
      Set%response.Headers("Access-Control-Allow-Origin")="*"
     
      Setqry="SELECT PREDICT(MaternalRiskModel) As PredictedMaternalRisk, "
                  _"age, BS, BodyTemp, DiastolicBP, HeartRate, SystolicBP "
                  _"FROM (SELECT "_data.BS_" AS BS, "
                  _data.BodyTemp_" As BodyTemp, "
                  _data.DiastolicBP_" AS DiastolicBP, "
                  _data.HeartRate_" AS HeartRate, "
                  _data.SystolicBP_" As SystolicBP, "
                  _data.Age_" AS age)"
      SettStatement=##class(%SQL.Statement).%New()
      SetqStatus=tStatement.%Prepare(qry)
      IfqStatus'=1{WRITE"%Prepare failed:"DO$System.Status.DisplayError(qStatus)QUIT}
      Setrset=tStatement.%Execute()
      Dorset.%Next()
 
      SetResponse={}
      SetResponse.PredictedMaternalRisk=rset.PredictedMaternalRisk
      SetResponse.Age=rset.Age
      SetResponse.SystolicBP=rset.SystolicBP
      SetResponse.DiastolicBP=rset.DiastolicBP
      SetResponse.BS=rset.BS
      SetResponse.BodyTemp=rset.BodyTemp
      SetResponse.HeartRate=rset.HeartRate
 
      WriteResponse.%ToJSON()
      Return1
     
    }Catcherr{
      write!,"Error name: ",?20,err.Name,
          !,"Error code: ",?20,err.Code,
          !,"Error location: ",?20,err.Location,
          !,"Additional data: ",?20,err.Data,!
      Return
    }
}

これで、どの Web アプリケーションもこの予測を使用して、結果を表示できるようになりました。 predict-diseases アプリケーションのソースコードは、frontend フォルダをご覧ください。

0
0 144
記事 Toshihiko Minamoto · 2月 14, 2022 19m read

キーワード: IRIS、IntegratedML、Flask、FastAPI、Tensorflow Serving、HAProxy、Docker、Covid-19

目的:

過去数か月に渡り、潜在的なICU入室を予測するための単純なCovid-19 X線画像分類器やCovid-19ラボ結果分類器など、ディープラーニングと機械学習の簡単なデモをいくつか見てきました。  また、ICU分類器のIntegratedMLデモ実装についても見てきました。  「データサイエンス」の旅路はまだ続いていますが、「データエンジニアリング」の観点から、AIサービスデプロイメントを試す時期が来たかもしれません。これまでに見てきたことすべてを、一式のサービスAPIにまとめることはできるでしょうか。  このようなサービススタックを最も単純なアプローチで達成するには、どういった一般的なツール、コンポーネント、およびインフラストラクチャを活用できるでしょうか。

対象範囲

対象:

ジャンプスタートとして、docker-composeを使用して、次のDocker化されたコンポーネントをAWS Ubuntuサーバーにデプロイできます。

  • **HAProxy ** - ロードバランサー
  • Gunicorn と **Univorn ** - Webゲートウェイ****サーバー
  • FlaskFastAPI - WebアプリケーションUI、サービスAPI定義、およびヒートマップ生成などのアプリケーションサーバー
  • Tensorflow Model ServingTensorflow-GPU Model Serving - 画像や分類などのアプリケーションバックエンドサーバー
  • IRIS IntegratedML - SQL インターフェースを備えたアプリとデータベースを統合した AutoML
  • ベンチマーキング用クライアントをエミュレートする、JupyterノートブックのPython3
  •  Dockerとdocker-compose
  • Testla T4 GPU搭載のAWS Ubuntu 16.04 

注意: GPUを使用したTensorflow Servingはデモのみを目的としています。GPU関連の画像(dockerfile)と構成(docker-compose.yml)は、単純にオフにできます。

対象外またはウィッシュリスト:

  • Nginx またはApacheなどのWebサーバーは、今のところこのデモでは省略されています。
  • RabbitMQとRedis  - IRISまたはEnsembleと置き換えられる、信頼性の高いメッセージングキューブローカー。   
  • IAMIntersystems API Manger)またはKongはウィッシュリストに含まれます。
  • **SAM **(InterSystemsのシステムアラートと監視) 
  • Kubernetes Operator付きのICMInterSystems Cloud Manager)- 誕生したときからずっとお気に入りの1つです。
  • FHIR(IntesyStems IRISベースのFHIR R4サーバーとFHIRアプリのSMART用FHIRサンドボックス)
  • CI/CD開発ツールまたはGithub Actions

「機械学習エンジニア」は、サービスのライフサイクルに沿って本番環境をプロビジョニングする際に、必然的にこれらのすべてのコンポーネントを操作しなければならないでしょう。 今後徐々に、焦点を当てていきたいと思います。

GitHubリポジトリ

全ソースコードの場所: https://github.com/zhongli1990/covid-ai-demo-deployment

また、新しいリポジトリとともに、integratedML-demo-templateリポジトリも再利用します。
 

デプロイメントのパターン

以下に、この「DockerでのAIデモ」テストフレームワークの論理的なデプロイパターンを示します。

デモの目的により、意図的にディープラーニング分類とWebレンダリング用に個別のスタックを2つ作成し、HAProxyをソフトロードバランサーとして使用して、受信するAPIリクエストをこれらの2つのスタックでステートレスに分散できるようにしました。

  • Guniorn + Flask + Tensorflow Serving
  • Univcorn + FaskAPI + Tensorflow Serving GPU

前の記事のICU入室予測と同様に、機械学習デモのサンプルには、IntegratedMLを使用したIRISを使用します。

現在のデモでは、本番サービスでは必要または検討される共通コンポーネントをいくつか省略しました。

  • Webサーバー: NginxまたはApacheなど。 HAProxyとGunicorn/Uvicornの間で、適切なHTTPセッション処理を行うために必要となります(DoS攻撃を防止するなど)。
  • キューマネージャーとDB: RabbitMQやRedisなど。Flask/FastAPIとバックエンドサービングの間で、信頼性のある非同期サービングとデータ/構成の永続性などに使用されます。  
  • APIゲートウェイ: IAMまたはKongクラスター。単一障害点を作らないように、HAProxyロードバランサーとAPI管理用Webサーバーの間に使用されます。
  • 監視とアラート: SAMが適切でしょう。
  • CI/CD開発のプロビジョニング: クラウドニューラルデプロイメントと管理、およびその他の一般的な開発ツールによるCI/CDにはK8を使用したICMが必要です。

実際のところ、IRIS自体は、信頼性の高いメッセージングのためのエンタープライズ級のキューマネージャーとしても、高性能データベースとしても確実に使用することができます。 パターン分析では、IRISがRabbitMQ/Redis/MongoDBなどのキューブローカーとデータベースの代わりになることは明らかであるため、レイテンシが大幅に低く、より優れたスループットパフォーマンスとさらに統合する方が良いでしょう。  さらに、IRIS Webゲートウェイ(旧CSPゲートウェイ)は、GunicornやUvicornなどの代わりに配置できますよね?  

環境トポロジー

上記の論理パターンを全Dockerコンポーネントに実装するには、いくつかの一般的なオプションがあります。 頭に思い浮かぶものとしては以下のものがあります。  

  • docker-compose
  • docker swarmなど
  • Kubernetesなど 
  • K8演算を使用したICM

このデモは、機能的なPoCといくつかのベンチマーキングを行うために、「docker-compose」から始めます。 もちろん、いつかはK8やICMを使いたいとも考えています。 

docker-compose.ymlファイルに説明されているように、AWS Ubuntuサーバーでの環境トポロジーの物理的な実装は、以下のようになります。  

上図は、全Dockerインスタンスのサービスポートが、デモの目的でどのようにマッピングされており、Ubuntuサーバーで直接公開されているかを示したものです。 本番環境では、すべてセキュリティ強化される必要があります。 また、純粋なデモの目的により、すべてのコンテナは同じDockerネットワークに接続されています。本番環境では、外部ルーティング可能と内部ルーティング不可能として分離されます。

Docker化コンポーネント 

以下は、docker-compose.ymlファイルに示されるとおり、ホストマシン内でこれらのストレージボリュームが各コンテナインスタンスにどのようにマウントされているかを示します。 

ubuntu@ip-172-31-35-104:/zhong/flask-xray$ tree ./ -L 2

./├── covid19                             (Flask+GunicornコンテナとTensorflow Servingコンテナがここにマウントされます)├── app.py                                (Flaskメインアプリ:  WebアプリケーションとAPIサービスインターフェースの両方がここに定義されて実装されます)├── covid19_models               (CPU使用の画像分類Tensorflow Servingコンテナ用のTensorflowモデルはここに公開されてバージョン管理されます)├── Dockerfile                          (Flask サーバーとGunicorn:      CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:5000", "--workers", "4", "--threads", "2"]├── models                               (.h5形式のFlaskアプリ用モデルとX線画像へのGrad-CAMによるヒートマップ生成のAPIデモ)├── __pycache__├── README.md├── requirements.txt             (全Flask+Gunicornアプリに必要なPythonパッケージ) ├── scripts├── static                                  (Web静的ファイル)├── templates                         (Webレンダリングテンプレート)├── tensorflow_serving        (TensorFlow Servingサービスの構成ファイル)└── test_images├── covid-fastapi                   (FastAPI+UvicornコンテナとGPU使用のTensorflow Servingコンテナはここにマウントされます)├── covid19_models            (画像分類用のTensorflow Serving GPUモデルは、ここに公開されてバージョン管理されます)├── Dockerfile                       (Uvicorn+FastAPIサーバーはここから起動されます: CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4" ]├── main.py                           (FastAPIアプリ: WebアプリケーションとAPIサービスインターフェースの両方がここに定義されて実装されます)├── models                            (.h5形式のFastAPIアプリ用モデルとX線画像へのGrad-CAMによるヒートマップ生成のAPIデモ)├── __pycache__├── README.md├── requirements.txt├── scripts├── static├── templates├── tensorflow_serving└── test_images├── docker-compose.yml      (フルスタックDocker定義ファイル。  Docker GPU「nvidia runtime」用にバージョン2.3を使用。そうでない場合はバージョン3.xを使用可)├── haproxy                             (HAProxy dockerサービスはここに定義されます。  注意: バックエンドLBにはスティッキーセッションを定義できます。 )                             ├── Dockerfile└── haproxy.cfg└── notebooks                       (TensorFlow 2.2とTensorBoardなどを含むJupyterノートブックコンテナサービス)├── Dockerfile├── notebooks                  (機能テスト用に外部APIクライアントアプリをエミュレートするサンプルノートブックファイルとロードバランサーに対してPythonによるAPIベンチマークテストを行うサンプルノートブックファイル)└── requirements.txt

注意: 上記のdocker-compose.ymlはCovid-19 X線画像のディープラーニングデモ用です。  別のintegratedML-demo-templatedocker-compose.ymlとともに、環境トポロジーに表示されるフルサービススタックを形成するために使用されています。  

サービスの起動 

すべてのコンテナサービスを起動するには、単純なdocker-compose up -dを使用します。

ubuntu@ip-172-31-35-104:~$ docker ps
CONTAINER ID        IMAGE                                 COMMAND                  CREATED             STATUS                PORTS                                                                              NAMES
31b682b6961d        iris-aa-server:2020.3AA               "/iris-main"             7 weeks ago         Up 2 days (healthy)   2188/tcp, 53773/tcp, 54773/tcp, 0.0.0.0:8091->51773/tcp, 0.0.0.0:8092->52773/tcp   iml-template-master_irisimlsvr_1
6a0f22ad3ffc        haproxy:0.0.1                         "/docker-entrypoint.…"   8 weeks ago         Up 2 days             0.0.0.0:8088->8088/tcp                                                             flask-xray_lb_1
71b5163d8960        ai-service-fastapi:0.2.0              "uvicorn main:app --…"   8 weeks ago         Up 2 days             0.0.0.0:8056->8000/tcp                                                             flask-xray_fastapi_1
400e1d6c0f69        tensorflow/serving:latest-gpu         "/usr/bin/tf_serving…"   8 weeks ago         Up 2 days             0.0.0.0:8520->8500/tcp, 0.0.0.0:8521->8501/tcp                                     flask-xray_tf2svg2_1
eaac88e9b1a7        ai-service-flask:0.1.0                "gunicorn app:app --…"   8 weeks ago         Up 2 days             0.0.0.0:8051->5000/tcp                                                             flask-xray_flask_1
e07ccd30a32b        tensorflow/serving                    "/usr/bin/tf_serving…"   8 weeks ago         Up 2 days             0.0.0.0:8510->8500/tcp, 0.0.0.0:8511->8501/tcp                                     flask-xray_tf2svg1_1
390dc13023f2        tf2-jupyter:0.1.0                     "/bin/sh -c '/bin/ba…"   8 weeks ago         Up 2 days             0.0.0.0:8506->6006/tcp, 0.0.0.0:8586->8888/tcp                                     flask-xray_tf2jpt_1
88e8709404ac        tf2-jupyter-jdbc:1.0.0-iml-template   "/bin/sh -c '/bin/ba…"   2 months ago        Up 2 days             0.0.0.0:6026->6006/tcp, 0.0.0.0:8896->8888/tcp                                     iml-template-master_tf2jupyter_1

docker-compose up --scale fastapi=2 --scale flask=2 -d   for example will horizontally scale up to 2x Gunicorn+Flask containers and 2x Univcorn+FastAPI containers:

ubuntu@ip-172-31-35-104:/zhong/flask-xray$ docker ps
CONTAINER ID        IMAGE                                 COMMAND                  CREATED             STATUS                PORTS                                                                              NAMES
dbee3c20ea95        ai-service-fastapi:0.2.0              "uvicorn main:app --…"   4 minutes ago       Up 4 minutes          0.0.0.0:8057->8000/tcp                                                             flask-xray_fastapi_2
95bcd8535aa6        ai-service-flask:0.1.0                "gunicorn app:app --…"   4 minutes ago       Up 4 minutes          0.0.0.0:8052->5000/tcp                                                             flask-xray_flask_2

... ...

「integrtedML-demo-template」の作業ディレクトリで別の「docker-compose up -d」を実行することで、上記リストのirisimlsvrとtf2jupyterコンテナが起動されています。

テスト

1. 単純なUIを備えたAIデモWebアプリ

上記のdockerサービスを起動したら、一時アドレスhttp://ec2-18-134-16-118.eu-west-2.compute.amazonaws.com:8056/のAWS EC2インスタンスにホストされているCovid-19に感染した胸部X線画像の検出用デモWebアプリにアクセスできます。

以下は、私の携帯でキャプチャした画面です。  デモUIは非常に単純です。基本的に「Choose File」をクリックして「Submit」ボタンを押せば、X線画像がアップロードされて分類レポートが表示されます。 Covid-19感染のX線画像として分類されたら、DLによって「検出された」病変領域をエミュレートするヒートマップが表示されます。そうでない場合は、分類レポートにはアップロードされたX線画像のみが表示されます。

        

このWebアプリはPythonサーバーページです。このロジックは主にFastAPI's main.pyファイルとFlask's app.pyファイルにコーディングされています。

もう少し時間に余裕がある際には、FlaskとFastAPIのコーディングと規則の違いを詳しく説明かもしれません。  実は、AIデモホスティングについて、FlaskとFastAPIとIRISの比較を行いたいと思っています。 

2. テストデモAPI      

FastAPI(ポート8056で公開)には、以下に示すSwagger APIドキュメントが組み込まれています。 これは非常に便利です。 以下のようにURLに「/docs」を指定するだけで、利用することができます。 

いくつかのプレースホルダー(/helloや/itemsなど)と実際のデモAPIインターフェース(/healthcheck、/predict、predict/heatmapなど)を組み込みました。

これらのAPIに簡単なテストを実行してみましょう私がこのAIデモサービス用に作成したJupyterノートブックサンプルの1つで、Python行を(APIクライアントアプリエミュレーターとして)いくつか実行します。  

以下では、例としてこちらのファイルを実行しています。 https://github.com/zhongli1990/covid-ai-demo-deployment/blob/master/notebooks/notebooks/Covid19-3class-Heatmap-Flask-FastAPI-TF-serving-all-in-one-HAProxy2.ipynb

まず、バックエンドのTF-Serving(ポート8511)とTF-Serving-GPU(ポート8521)が稼働していることをテストします。 

!curl http://172.17.0.1:8511/v1/models/covid19  # tensorflow serving
!curl http://172.17.0.1:8521/v1/models/covid19  # tensorflow-gpu serving
{
 "model_version_status": [
  {
   "version": "2",
   "state": "AVAILABLE",
   "status": {
    "error_code": "OK",
    "error_message": ""
   }
  }
 ]
}
{
 "model_version_status": [
  {
   "version": "2",
   "state": "AVAILABLE",
   "status": {
    "error_code": "OK",
    "error_message": ""
   }
  }
 ]
}

 

次に、以下のサービスAPIが稼働していることをテストします。

  • Gunicorn+Flask+TF-Serving
  • Unicorn+FastAPI+TF-Serving-GPU
  • 上記の両サービスの手間にあるHAProxyロードバランサー
  • r = requests.get('http://172.17.0.1:8051/covid19/api/v1/healthcheck')  # tf srving docker with cpu 
    print(r.status_code, r.text)
    r = requests.get('http://172.17.0.1:8056/covid19/api/v1/healthcheck')  # tf-serving docker with gpu
    print(r.status_code, r.text)
    r = requests.get('http://172.17.0.1:8088/covid19/api/v1/healthcheck')  # tf-serving docker with HAproxy
    print(r.status_code, r.text)

    そして、以下のような結果が期待されます。

    200 Covid19 detector API is live!
    200 "Covid19 detector API is live!\n\n"
    200 "Covid19 detector API is live!\n\n"

     

    入力X線画像の分類とヒートマップ結果を返す、/predict/heatmapなどの機能的なAPIインターフェースをテストします。  受信する画像は、API定義に従ってHTTP POST経由で送信される前にbased64にエンコードされています。

    %%time# リクエストライブラリをインポート
    import argparse
    import base64import requests# api-endpointを定義
    API_ENDPOINT = "http://172.17.0.1:8051/covid19/api/v1/predict/heatmap"image_path = './Covid_M/all/test/covid/nejmoa2001191_f3-PA.jpeg'
    #image_path = './Covid_M/all/test/normal/NORMAL2-IM-1400-0001.jpeg'
    #image_path = './Covid_M/all/test/pneumonia_bac/person1940_bacteria_4859.jpeg'
    b64_image = ""
    # JPGやPNGなどの画像をbase64形式にエンコード
    with open(image_path, "rb") as imageFile:
        b64_image = base64.b64encode(imageFile.read())# APIに送信されるデータ
    data = {'b64': b64_image}# POSTリクエストを送信しレスポンスをレスポンスオブジェクトとして保存
    r = requests.post(url=API_ENDPOINT, data=data)print(r.status_code, r.text)# レスポンスを抽出
    print("{}".format(r.text))

    このようなすべてのテスト画像もGitHubにアップロード済みです。  上記のコードの結果は以下のようになります。

    200 {"Input_Image":"http://localhost:8051/static/source/0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png","Output_Heatmap":"http://localhost:8051/static/result/Covid19_98_0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png.png","X-Ray_Classfication_Raw_Result":[[0.805902302,0.15601939,0.038078323]],"X-Ray_Classification_Covid19_Probability":0.98,"X-Ray_Classification_Result":"Covid-19 POSITIVE","model_name":"Customised Incpetion V3"}
    
    {"Input_Image":"http://localhost:8051/static/source/0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png","Output_Heatmap":"http://localhost:8051/static/result/Covid19_98_0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png.png","X-Ray_Classfication_Raw_Result":[[0.805902302,0.15601939,0.038078323]],"X-Ray_Classification_Covid19_Probability":0.98,"X-Ray_Classification_Result":"Covid-19 POSITIVE","model_name":"Customised Incpetion V3"}
    
    CPU times: user 16 ms, sys: 0 ns, total: 16 ms
    Wall time: 946 ms

     

    3. デモサービスAPIをベンチマークテストする

    HAProxyロードバランサーインスタンスをセットアップします。 また、Flaskサービスを4個のワーカーで開始し、FastAPIサービスも4個のワーカーで開始しました。

    ノートブックファイルで直接8個のPythonプロセスを作成し、8個の同時APIクライアントがデモサービスAPIにリクエストを送信する様子をエミュレートしてみてはどうでしょうか。 

    #from concurrent.futures import ThreadPoolExecutor as PoolExecutor
    from concurrent.futures import ProcessPoolExecutor as PoolExecutor
    import http.client
    import socket
    import timestart = time.time()#laodbalancer:
    API_ENDPOINT_LB = "http://172.17.0.1:8088/covid19/api/v1/predict/heatmap"
    API_ENDPOINT_FLASK = "http://172.17.0.1:8052/covid19/api/v1/predict/heatmap"
    API_ENDPOINT_FastAPI = "http://172.17.0.1:8057/covid19/api/v1/predict/heatmap"
    def get_it(url):
        try:
            # 画像をループ
            for imagePathTest in imagePathsTest:
                b64_image = ""
                with open(imagePathTest, "rb") as imageFile:
                    b64_image = base64.b64encode(imageFile.read())
        
                data = {'b64': b64_image}
                r = requests.post(url, data=data)
                #print(imagePathTest, r.status_code, r.text)
            return r
        except socket.timeout:
            # 実際のケースではおそらく
            # ソケットがタイムアウトする場合に何かを行うでしょう
            passurls = [API_ENDPOINT_LB, API_ENDPOINT_LB, 
            API_ENDPOINT_LB, API_ENDPOINT_LB, 
            API_ENDPOINT_LB, API_ENDPOINT_LB, 
            API_ENDPOINT_LB, API_ENDPOINT_LB]with PoolExecutor(max_workers=16) as executor:
        for _ in executor.map(get_it, urls):
            pass
        
    print("--- %s seconds ---" % (time.time() - start))

    したがって、8x27 = 216個のテスト画像を処理するのに74秒かかっています。 これは負荷分散されたデモスタックが、(分類とヒートマップの結果をクライアントに返すことで)1秒間に3個の画像を処理できています。

    --- 74.37691688537598 seconds ---

    PuttyセッションのTopコマンドから、上記のベンチマークスクリプトが実行開始されるとすぐに8個のサーバープロセス(4個のGunicornと4個のUvicorn/Python)がランプアップし始めたことがわかります。

    今後の内容

    この記事は、テストフレームワークとして「全DockerによるAIデモ」のデプロイメントスタックをまとめるための出発点に過ぎません。 次は、Covid-19 ICU入室予測インターフェースなどさらに多くのAPIデモインターフェースを理想としてはFHIR R4などによって追加し、サポートDICOM入力形式を追加したいと思います。 これは、IRISがホストするML機能とのより緊密な統合を検討するためのテストベンチになる可能性もあります。 医用画像、公衆衛生、またはパーソナル化された予測やNLPなど、さまざまなAIフロントを見ていく過程で、徐々に、より多くのMLまたはDL特殊モデルを傍受するためのテストフレームワーク(非常に単純なテストフレーム)として使用していけるでしょう。 ウィッシュリストは、前の投稿(「今後の内容」セクション)の最後にも掲載しています。 

    0
    0 550
    記事 Toshihiko Minamoto · 1月 25, 2022 9m read

    キーワード: PyODBC、unixODBC、IRIS、IntegratedML、Jupyterノートブック、Python 3

    目的

    数か月前、私は「IRISデータベースへのPython JDBC接続」という簡易メモを書きました。以来、PCの奥深くに埋められたスクラッチパッドよりも、その記事を頻繁に参照しています。 そこで今回は、もう一つの簡易メモで「IRISデータベースへのPython ODBC接続」を作成する方法を説明します。

    ODBCとPyODCBをWindowsクライアントでセットアップするのは非常に簡単なようですが、Linux/Unix系サーバーでunixODBCとPyODBCクライアントをセットアップする際には毎回、どこかで躓いてしまいます。

    バニラLinuxクライアントで、IRISをインストールせずに、リモートIRISサーバーに対してPyODBC/unixODBCの配管をうまく行うための単純で一貫したアプローチがあるのでしょうか。

    範囲

    最近、Linux Docker環境のJupyterノートブック内でゼロからPyODBCデモを機能させるようにすることに少しばかり奮闘したことがありました。 そこで、少し冗長的ではありますが、後で簡単に参照できるように、これをメモに残しておくことにしました。  

    範囲内 

    このメモでは、以下のコンポーネントに触れます。

  • PyODBC over unixODBC
  • TensorFlow 2.2とPython 3を使ったJupyter Notebookサーバー
  • サンプルテストデータを含むIntegratedMLを使ったIRIS2020.3 CEサーバー
  • この環境内で:

  • AWS Ubuntu 16.04におけるdocker-composeによるDockerエンジン 
  • Docker Desktop for MacOS、およびDocker Toolbox for Windows 10もテストされます。
  • 範囲外:

    繰り返しになりますが、このデモ環境で機能しない部分は評価されません。 それらは重要なことであり、以下のようにサイト固有の機能である可能性があります。

  • エンドツーエンドのセキュリティと監査
  • パフォーマンスとスケーラビリティ
  • ライセンスとサポート可能性など
  • 環境

    以下の構成とテスト手順では、任意のバニラLinux Dockerイメージを使用できますが、以下のようにすると、そのような環境を5分で簡単にセットアップできます。

    1.  このデモテンプレートをGit cloneします。
    2.  クローンされた、docker-compose.ymlファイルを含むディレクトリで"docker-compose up -d"を実行します。

    以下のトポロジーのように、2つのコンテナーのデモ環境が作成されます。 1つはPyODBCクライアントとしてのJupyter Notebookサーバー用で、もう1つはIRIS2020.3 CEサーバー用です。

    上記の環境では、tf2jupyterには"Python over JDBC"クライアント構成しか含まれておらず、ODBCまたはPyODBCクライアント構成はまだ含まれていません。

    そこで、わかりやすくするために、以下の手順を実行して、Jupyter Notebook内から直接それらをセットアップしましょう。  

    手順

    AWS Ubuntu 16.04サーバーで、以下の構成とテストを実行しました。 私の同僚の@Thomas.Dyarは、MacOSで実行しました。 また、Docker Toolbox for Windowsでも簡単にテストされていますが、 何らかの問題に遭遇した場合は、お知らせください。

    以下の手順は、Dockerfileに簡単に自動化できます。 ここでは、どのように行われたかを数か月後に忘れてしまった場合に備えて、手動で記録しました。

    1. 公式ドキュメント

  • IRISのODBCサポート
  • UnixにおけるODBCデータソースの定義 
  • IRISのPyODBCサポート 
  • 2. Jupyterサーバーに接続する

    私はローカルのPuttyのSSHトンネリングを使用してリモートのAWS Ubuntuポート22に接続し、上記のトポロジーのようにポート8896にマッピングしました。

    (ローカルのdocker環境では、たとえば、直接dockerマシンのIP:8896にHTTP接続することもできます。)

    3. Jupyterノートブック内からODBCインストールを実行する

    Jupyterのセル内から直接以下を実行します。 

    !apt-get update&lt;br>!apt-get install gcc&lt;br>!apt-get install -y tdsodbc unixodbc-dev&lt;br>!apt install unixodbc-bin -y&lt;br>!apt-get clean -y

    上記は、次の手順でPyODBCドライバーをリコンパイルするために必要なgcc(g++を含む)、FreeTDS、unixODBC、およびunixodbc-devをインストールします。

    この手順はネイティブWindowsサーバーまたはPCでのPyODBCインストールには必要ありません。 

    4. Jupyter内からPyODBCインストールを実行する

    !pip install pyodbc
    Collecting pyodbc
      Downloading pyodbc-4.0.30.tar.gz (266 kB)
         |████████████████████████████████| 266 kB 11.3 MB/s eta 0:00:01
    Building wheels for collected packages: pyodbc
      Building wheel for pyodbc (setup.py) ... done
      Created wheel for pyodbc: filename=pyodbc-4.0.30-cp36-cp36m-linux_x86_64.whl size=273453 sha256=b794c35f41e440441f2e79a95fead36d3aebfa74c0832a92647bb90c934688b3
      Stored in directory: /root/.cache/pip/wheels/e3/3f/16/e11367542166d4f8a252c031ac3a4163d3b901b251ec71e905
    Successfully built pyodbc
    Installing collected packages: pyodbc
    Successfully installed pyodbc-4.0.30

    上記は、このDockerデモ用に最小化されたpip installです。 公式ドキュメントには、「MacOS Xインストール」用のより詳細なpip installが提供されています。

    5. LinuxでODBC INIファイルとリンクを再構成する

    以下を実行して、odbcinst.iniodbc.iniリンクを再作成します。

    !rm /etc/odbcinst.ini
    !rm /etc/odbc.ini
    !ln -s /tf/odbcinst.ini /etc/odbcinst.ini
    !ln -s /tf/odbc.ini /etc/odbc.ini

    注意: 上記を行う理由は、手順3と4では通常2つの空の(したがって無効な)ODBCファイルが\etc\ディレクトリに作成されるためです。Windowsインストールとは異なりこれらの空のiniファイルは問題を生じるため、まずそれらを削除してから、マッピングされたDockerボリュームに提供されている実際のiniファイル(/tf/odbcinst.ini, and /tf/odbc.ini)へのリンクを再作成してください。

    これらの2つのiniファイルをチェックしましょう。この場合、Linux ODBC構成の最も単純な形式です。

    !cat /tf/odbcinst.ini
    [InterSystems ODBC35]
    UsageCount=1
    Driver=/tf/libirisodbcu35.so
    Setup=/tf/libirisodbcu35.so
    SQLLevel=1
    FileUsage=0
    DriverODBCVer=02.10
    ConnectFunctions=YYN
    APILevel=1
    DEBUG=1
    CPTimeout=&lt;not pooled>
    !cat /tf/odbc.ini
    [IRIS PyODBC Demo]
    Driver=InterSystems ODBC35
    Protocol=TCP
    Host=irisimlsvr
    Port=51773
    Namespace=USER
    UID=SUPERUSER
    Password=SYS
    Description=Sample namespace
    Query Timeout=0
    Static Cursors=0

    上記のファイルは事前構成済みであり、マッピングされたドライブで提供されています。 IRISサーバーのコンテナーインスタンスからも取得できるドライバーファイルlibirisodbcu35.soを参照しています。

    したがって、上記のODBCインストールを機能させるには、これらの3つのファイルがマッピングされたドライブ(または任意のLinuxドライブ)に存在し、適切なファイルアクセス権が適用されていることが必要です。

  • libirisodbcu35.so
  • odbcinst.ini
  • odbc.ini
  • **6. PyODBCのインストールを検証する **

    !odbcinst -j
    unixODBC 2.3.4
    DRIVERS............: /etc/odbcinst.ini
    SYSTEM DATA SOURCES: /etc/odbc.ini
    FILE DATA SOURCES..: /etc/ODBCDataSources
    USER DATA SOURCES..: /root/.odbc.ini
    SQLULEN Size.......: 8
    SQLLEN Size........: 8
    SQLSETPOSIROW Size.: 8
    import pyodbc
    print(pyodbc.drivers())
    ['InterSystems ODBC35']

    上記の出力では、現在ODBCドライバーに有効なリンクがあることが示されています。

    Jupyter NotebookでPython ODBCテストを実行できるはずです。

    7. IRISサンプルへのPython ODBC接続を実行する

    import pyodbc 
    import time
    ### 1. Get an ODBC connection 
    #input("Hit any key to start")
    dsn = 'IRIS PyODBC Demo'
    server = 'irisimlsvr'   # IRIS server container or the docker machine's IP 
    port = '51773'   # or 8091 if docker machine IP is used
    database = 'USER' 
    username = 'SUPERUSER' 
    password = 'SYS' 
    #cnxn = pyodbc.connect('DSN='+dsn+';')   # use the user DSN defined in odbc.ini, or use the connection string below
    cnxn = pyodbc.connect('DRIVER={InterSystems ODBC35};SERVER='+server+';PORT='+port+';DATABASE='+database+';UID='+username+';PWD='+ password)
    ###ensure it reads strings correctly.
    cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf8')
    cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf8')
    cnxn.setencoding(encoding='utf8')
    ### 2. Get a cursor; start the timer
    cursor = cnxn.cursor()
    start= time.clock()
    ### 3. specify the training data, and give a model name
    dataTable = 'DataMining.IrisDataset'
    dataTablePredict = 'Result12'
    dataColumn =  'Species'
    dataColumnPredict = "PredictedSpecies"
    modelName = "Flower12" #chose a name - must be unique in server end
    ### 4. Train and predict
    #cursor.execute("CREATE MODEL %s PREDICTING (%s)  FROM %s" % (modelName, dataColumn, dataTable))
    #cursor.execute("TRAIN MODEL %s FROM %s" % (modelName, dataTable))
    #cursor.execute("Create Table %s (%s VARCHAR(100), %s VARCHAR(100))" % (dataTablePredict, dataColumnPredict, dataColumn))
    #cursor.execute("INSERT INTO %s  SELECT TOP 20 PREDICT(%s) AS %s, %s FROM %s" % (dataTablePredict, modelName, dataColumnPredict, dataColumn, dataTable)) 
    #cnxn.commit()
    ### 5. show the predict result
    cursor.execute("SELECT * from %s ORDER BY ID" % dataTable)   #or use dataTablePredict result by IntegratedML if you run step 4 above
    row = cursor.fetchone() 
    while row: 
        print(row) 
        row = cursor.fetchone()
    ### 6. CLose and clean     
    cnxn.close()
    end= time.clock()
    print ("Total elapsed time: ")
    print (end-start)
    (1, 1.4, 0.2, 5.1, 3.5, 'Iris-setosa')
    (2, 1.4, 0.2, 4.9, 3.0, 'Iris-setosa')
    (3, 1.3, 0.2, 4.7, 3.2, 'Iris-setosa')
    (4, 1.5, 0.2, 4.6, 3.1, 'Iris-setosa')
    (5, 1.4, 0.2, 5.0, 3.6, 'Iris-setosa')
    ... ...
    ... ...
    ... ...
    (146, 5.2, 2.3, 6.7, 3.0, 'Iris-virginica')
    (147, 5.0, 1.9, 6.3, 2.5, 'Iris-virginica')
    (148, 5.2, 2.0, 6.5, 3.0, 'Iris-virginica')
    (149, 5.4, 2.3, 6.2, 3.4, 'Iris-virginica')
    (150, 5.1, 1.8, 5.9, 3.0, 'Iris-virginica')
    Total elapsed time: 
    0.023873000000000033

    ここにはいくつかの落とし穴があります。

    1. **cnxn = pyodbc.connect() ** - Linux環境では、この呼び出しに渡される接続文字列は、スペースなしで文字通り正しい必要があります。  
    2. 接続エンコーディングをたとえばuft8などで適切に設定してください。  この場合は、文字列のデフォルトエンコーディングは機能しません。
    3. libirisodbcu35.so - 理想的には、このドライバーファイルはリモートIRISサーバーのバージョンと緊密に連携する必要があります。  

    **今後の内容 **

    これで、リモートIRISサーバーへのPyODBC(およびJDBC)接続による、Python 3とTensorFlow 2.2(GPUなし)を含むJupyterノートブックのDocker環境を得られました。 IRIS IntegratedML固有のSQL構文など、特別に設計されたすべてのSQL構文で機能するはずです。そこで、IntegratedMLの機能をもう少し探り、MLライフサイクルを駆動するSQL手法を工夫してみてはどうでしょうか。 

    また、次回は、Python環境でIRISネイティブSQLまたはマジックSQLを使用してIRISサーバーに接続する上で、最も単純なアプローチに触れられればと思います。 また、今では優れたPython Gatewayを使用できるため、外部のPython MLアプリケーションとサービスをIRISサーバー内から直接呼び出してみることも可能です。これについてもさらに詳しく試せればいいなと思っています。

    付録

    上記のノートブックファイルは、こちらのGitHubリポジトリとOpen Exchangeにチェックインされます。

    0
    0 1188
    記事 Toshihiko Minamoto · 12月 7, 2021 31m read

    キーワード: IRIS、IntegratedML、機械学習、Covid-19、Kaggle 

    目的

    最近、Covid-19患者がICU(集中治療室)に入室するかどうかを予測するKaggleデータセットがあることに気づきました。  231列のバイタルサインや観測で構成される1925件の遭遇記録が含まれる表計算シートで、最後の「ICU」列では「Yes」を示す1と「No」を示す0が使用されています。 既知のデータに基づいて、患者がICUに入室するかどうかを予測することがタスクです。

    このデータセットは、「従来型ML」タスクと呼ばれるものの良い例のようです。 データ量は適切で、品質も比較的適切なようです。 IntegratedMLデモキットに直接適用できる可能性が高いようなのですが、通常のMLパイプラインと潜在的なIntegratedMLアプローチに基づいて簡易テストを行うには、どのようなアプローチが最も単純なのでしょうか。

    範囲

    次のような通常のMLステップを簡単に実行します。

    • データのEDA(探索的データ分析) 
    • 特徴量の選択
    • モデルの選択
    • グリッド検索によるモデルパラメーターのチューニング

    上記との比較で、次を実行します。 

    • SQLを介したIntegratedMLアプローチ

    Docker-composeなどを使用して、AWS Ubuntu 16.04サーバーで実行します。   

    環境

    integredML-demo-templateのDocker環境を再利用します。

    以下のノートブックファイルは「tf2jupyter」で実行しており、IRISとIntegratedMLは「irismlsrv」で実行しています。  Docker-composeはAWS Ubuntu 16.04で実行します。

    データとタスク

    このデータセットには、385人の患者から収集した1925件のレコードが含まれます。各患者にはちょうど5つの遭遇レコードがあります。 231列のうちの1つの列は、このトレーニングと予測のターゲットである「ICU」で、残りの230列は入力として使用することが可能です。 ICUは1または0のバイナリー値です。 カテゴリカル文字列(データフレームでは「オブジェクト」として表されます)のような2つの列を除くと、すべては数値データです。

    import numpy as np 
    import pandas as pd 
    from sklearn.impute import SimpleImputer
    import matplotlib.pyplot as plt
    from sklearn.linear_model import LogisticRegression
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import classification_report, roc_auc_score, roc_curve
    import seaborn as sns
    sns.set(style="whitegrid")
    
    import os
    for dirname, _, filenames in os.walk('./input'):
        for filename in filenames:
            print(os.path.join(dirname, filename))
    ./input/datasets_605991_1272346_Kaggle_Sirio_Libanes_ICU_Prediction.xlsx

     

    df = pd.read_excel("./input/datasets_605991_1272346_Kaggle_Sirio_Libanes_ICU_Prediction.xlsx")
    df
    <th>
      PATIENT_VISIT_IDENTIFIER
    </th>
    
    <th>
      AGE_ABOVE65
    </th>
    
    <th>
      AGE_PERCENTIL
    </th>
    
    <th>
      GENDER
    </th>
    
    <th>
      DISEASE GROUPING 1
    </th>
    
    <th>
      DISEASE GROUPING 2
    </th>
    
    <th>
      DISEASE GROUPING 3
    </th>
    
    <th>
      DISEASE GROUPING 4
    </th>
    
    <th>
      DISEASE GROUPING 5
    </th>
    
    <th>
      DISEASE GROUPING 6
    </th>
    
    <th>
      ...
    </th>
    
    <th>
      TEMPERATURE_DIFF
    </th>
    
    <th>
      OXYGEN_SATURATION_DIFF
    </th>
    
    <th>
      BLOODPRESSURE_DIASTOLIC_DIFF_REL
    </th>
    
    <th>
      BLOODPRESSURE_SISTOLIC_DIFF_REL
    </th>
    
    <th>
      HEART_RATE_DIFF_REL
    </th>
    
    <th>
      RESPIRATORY_RATE_DIFF_REL
    </th>
    
    <th>
      TEMPERATURE_DIFF_REL
    </th>
    
    <th>
      OXYGEN_SATURATION_DIFF_REL
    </th>
    
    <th>
      WINDOW
    </th>
    
    <th>
      ICU
    </th>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      60th
    </td>
    
    <td>
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      0-2
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      60th
    </td>
    
    <td>
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      2-4
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      60th
    </td>
    
    <td>
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      4-6
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      60th
    </td>
    
    <td>
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      6-12
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      60th
    </td>
    
    <td>
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -0.238095
    </td>
    
    <td>
      -0.818182
    </td>
    
    <td>
      -0.389967
    </td>
    
    <td>
      0.407558
    </td>
    
    <td>
      -0.230462
    </td>
    
    <td>
      0.096774
    </td>
    
    <td>
      -0.242282
    </td>
    
    <td>
      -0.814433
    </td>
    
    <td>
      ABOVE_12
    </td>
    
    <td>
      1
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      384
    </td>
    
    <td>
    </td>
    
    <td>
      50th
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      0-2
    </td>
    
    <td>
    </td>
    
    <td>
      384
    </td>
    
    <td>
    </td>
    
    <td>
      50th
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      2-4
    </td>
    
    <td>
    </td>
    
    <td>
      384
    </td>
    
    <td>
    </td>
    
    <td>
      50th
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      4-6
    </td>
    
    <td>
    </td>
    
    <td>
      384
    </td>
    
    <td>
    </td>
    
    <td>
      50th
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      6-12
    </td>
    
    <td>
    </td>
    
    <td>
      384
    </td>
    
    <td>
    </td>
    
    <td>
      50th
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -0.547619
    </td>
    
    <td>
      -0.838384
    </td>
    
    <td>
      -0.701863
    </td>
    
    <td>
      -0.585967
    </td>
    
    <td>
      -0.763868
    </td>
    
    <td>
      -0.612903
    </td>
    
    <td>
      -0.551337
    </td>
    
    <td>
      -0.835052
    </td>
    
    <td>
      ABOVE_12
    </td>
    
    <td>
    </td>
    
     
    1
    2
    3
    4
    ...
    1920
    1921
    1922
    1923
    1924

    1925 rows × 231 columns

    df.dtypes
    PATIENT_VISIT_IDENTIFIER        int64
    AGE_ABOVE65                     int64
    AGE_PERCENTIL                  object
    GENDER                          int64
    DISEASE GROUPING 1            float64
                                   ...   
    RESPIRATORY_RATE_DIFF_REL     float64
    TEMPERATURE_DIFF_REL          float64
    OXYGEN_SATURATION_DIFF_REL    float64
    WINDOW                         object
    ICU                             int64
    Length: 231, dtype: object

    この問題とそのアプローチを組むには、確実にいくつかのオプションがあります。 最初に頭に浮かぶのは、これが「二項分類」の問題と考えられることです。 すべての1925件のレコードを、同じ患者のレコードであるかに関係なく、「ステートレス」の個別のレコードとして扱うことができます。 もちろん、ICUと他の値をすべて数値として扱うのであれば、「回帰」問題としても考えられるでしょう。

    確かにほかのアプローチもあります。 たとえば、データセットに、患者ごとに異なる385件の短い「時系列」があるというように扱うこともできます。 セット全体をTrain/Val/Test用の385個の個別のセットに分解した場合は、CNNやLSTMなどのディープラーニングモデルを使って、個々の患者ごとに各セットに隠れた「症状の発展段階またはパターン」をキャプチャできるでしょうか? できるかもしれませんね。 それを行えば、データ拡張も適用して、様々な方法でテストデータを充実させることができるかもしれません。 これについては、この記事の対象外として別のトピックとしましょう。

    この記事では、いわゆる「従来型ML」とIntegratedML(AutoML)アプローチのクイックランをテストします。  

    「従来型」MLアプローチとは

    このデータセットは、いくつかの値が欠落していることを除き、ほとんどの実際のケースより比較的正規化されたものであるため、特徴量エンジニアリングの部分を省略して、直接列を特徴量として使用できる可能性があります。 では早速、特徴量の選択に進みましょう。

    欠落しているデータを代入する

    まず、すべての欠落している値が単純な代入で埋められていることを確認します。

    df_cat = df.select_dtypes(include=['object'])
    df_numeric = df.select_dtypes(exclude=['object'])
    imp = SimpleImputer(missing_values=np.nan, strategy='mean')
    idf = pd.DataFrame(imp.fit_transform(df_numeric))
    idf.columns = df_numeric.columns
    idf.index = df_numeric.index
    idf.isnull().sum()

     

    特徴量の選択

    データフレームに組み込まれている正規相関関数を使用して、ICUに対する各列の値の相関を計算できます。

    特徴量エンジニアリング - 相関 {#featuring-engineering---correlation}

    idf.drop(["PATIENT_VISIT_IDENTIFIER"],1)
    idf = pd.concat([idf,df_cat ], axis=1)
    cor = idf.corr()
    cor_target = abs(cor["ICU"])
    relevant_features = cor_target[cor_target>0.1]   # correlation above 0.1
    print(cor.shape, cor_target.shape, relevant_features.shape)
    #relevant_features.index
    #relevant_features.index.shape

    これにより、ICUのターゲット値との相関が >0.1である88個の特徴量がリストされます。  これらの列は、モデル入力として直接利用できます。

    また、従来型MLタスクで通常使用されているほかの「特徴量選択手法」もいくつか実行してみました。

    特徴量の選択 - カイ二乗 {#feature-selection---Chi-squared}

    from sklearn.feature_selection import SelectKBest
    from sklearn.feature_selection import chi2
    from sklearn.preprocessing import MinMaxScaler
    X_norm = MinMaxScaler().fit_transform(X)
    chi_selector = SelectKBest(chi2, k=88)
    chi_selector.fit(X_norm, y)
    chi_support = chi_selector.get_support()
    chi_feature = X.loc[:,chi_support].columns.tolist()
    print(str(len(chi_feature)), 'selected features', chi_feature)
    88 selected features ['AGE_ABOVE65', 'GENDER', 'DISEASE GROUPING 1', ... ... 'P02_VENOUS_MIN', 'P02_VENOUS_MAX', ... ... RATURE_MAX', 'BLOODPRESSURE_DIASTOLIC_DIFF', ... ... 'TEMPERATURE_DIFF_REL', 'OXYGEN_SATURATION_DIFF_REL']

    特徴量の選択 - ピアソン相関 

    def cor_selector(X, y,num_feats):
        cor_list = []
        feature_name = X.columns.tolist()
        # calculate the correlation with y for each feature
        for i in X.columns.tolist():
            cor = np.corrcoef(X[i], y)[0, 1]
            cor_list.append(cor)
        # replace NaN with 0
        cor_list = [0 if np.isnan(i) else i for i in cor_list]
        # feature name
        cor_feature = X.iloc[:,np.argsort(np.abs(cor_list))[-num_feats:]].columns.tolist()
        # feature selection? 0 for not select, 1 for select
        cor_support = [True if i in cor_feature else False for i in feature_name]
        return cor_support, cor_featurecor_support, cor_feature = cor_selector(X, y, 88)
    print(str(len(cor_feature)), 'selected features:  ', cor_feature)
    88 selected features:   ['TEMPERATURE_MEAN', 'BLOODPRESSURE_DIASTOLIC_MAX', ... ... 'RESPIRATORY_RATE_DIFF', 'RESPIRATORY_RATE_MAX']

    特徴量の選択 - 再帰的特徴量除去(RFE){#feature-selection---Recursive-Feature-Elimination-(RFE)}

    from sklearn.feature_selection import RFE
    from sklearn.linear_model import LogisticRegression
    rfe_selector = RFE(estimator=LogisticRegression(), n_features_to_select=88, step=100, verbose=5)
    rfe_selector.fit(X_norm, y)
    rfe_support = rfe_selector.get_support()
    rfe_feature = X.loc[:,rfe_support].columns.tolist()
    print(str(len(rfe_feature)), 'selected features: ', rfe_feature)
    Fitting estimator with 127 features.
    88 selected features:  ['AGE_ABOVE65', 'GENDER', ... ... 'RESPIRATORY_RATE_DIFF_REL', 'TEMPERATURE_DIFF_REL']

    特徴量の選択 - Lasso

    ffrom sklearn.feature_selection import SelectFromModel
    from sklearn.linear_model import LogisticRegression
    from sklearn.preprocessing import MinMaxScaler
    X_norm = MinMaxScaler().fit_transform(X)
    embeded_lr_selector = SelectFromModel(LogisticRegression(penalty="l2"), max_features=88)
    embeded_lr_selector.fit(X_norm, y)
    embeded_lr_support = embeded_lr_selector.get_support()
    embeded_lr_feature = X.loc[:,embeded_lr_support].columns.tolist()
    print(str(len(embeded_lr_feature)), 'selected features', embeded_lr_feature)
    65 selected features ['AGE_ABOVE65', 'GENDER', ... ... 'RESPIRATORY_RATE_DIFF_REL', 'TEMPERATURE_DIFF_REL']

    特徴量の選択 - RFツリーベース: SelectFromModel

    from sklearn.feature_selection import SelectFromModel
    from sklearn.ensemble import RandomForestClassifier
    embeded_rf_selector = SelectFromModel(RandomForestClassifier(n_estimators=100), max_features=227)
    embeded_rf_selector.fit(X, y)
    embeded_rf_support = embeded_rf_selector.get_support()
    embeded_rf_feature = X.loc[:,embeded_rf_support].columns.tolist()
    print(str(len(embeded_rf_feature)), 'selected features', embeded_rf_feature)
    48 selected features ['AGE_ABOVE65', 'GENDER', ... ... 'TEMPERATURE_DIFF_REL', 'OXYGEN_SATURATION_DIFF_REL']

    特徴量の選択 - LightGBMまたはXGBoost {#feature-selection---LightGBM-or-XGBoost}

    from sklearn.feature_selection import SelectFromModel
    from lightgbm import LGBMClassifierlgbc=LGBMClassifier(n_estimators=500, learning_rate=0.05, num_leaves=32, colsample_bytree=0.2,
                reg_alpha=3, reg_lambda=1, min_split_gain=0.01, min_child_weight=40)embeded_lgb_selector = SelectFromModel(lgbc, max_features=128)
    embeded_lgb_selector.fit(X, y)embeded_lgb_support = embeded_lgb_selector.get_support()
    embeded_lgb_feature = X.loc[:,embeded_lgb_support].columns.tolist()
    print(str(len(embeded_lgb_feature)), 'selected features:  ', embeded_lgb_feature)
    embeded_lgb_feature.index
    56 selected features:   ['AGE_ABOVE65', 'GENDER', 'HTN', ... ... 'TEMPERATURE_DIFF_REL', 'OXYGEN_SATURATION_DIFF_REL']

    特徴量の選択 - すべてを一緒に {#feature-selection---Ensemble-them-all}

    feature_name = X.columns.tolist()
    # put all selection together
    feature_selection_df = pd.DataFrame({'Feature':feature_name, 'Pearson':cor_support, 'Chi-2':chi_support, 'RFE':rfe_support, 'Logistics':embeded_lr_support, 'Random Forest':embeded_rf_support, 'LightGBM':embeded_lgb_support})
    # count the selected times for each feature
    feature_selection_df['Total'] = np.sum(feature_selection_df, axis=1)
    # display the top 100
    num_feats = 227
    feature_selection_df = feature_selection_df.sort_values(['Total','Feature'] , ascending=False)
    feature_selection_df.index = range(1, len(feature_selection_df)+1)
    feature_selection_df.head(num_feats)
    df_selected_columns = feature_selection_df.loc[(feature_selection_df['Total'] > 3)]
    df_selected_columns

    少なくとも4つの方法で選択された特徴量をリストできます。

    ... ...

    これらの58個の特徴量を選択できます。 一方で、経験からして、特徴量の選択は、必ずしも民主的投票だとは限りません。多くの場合はドメインの問題や特定のデータに特化するものであり、時には、この記事で後で採用する特定のMLモデルやアプローチに特化した選択手法であることもあります。

    特徴量の選択 - サードパーティツール 

    広く使用されている業界ツールやAutoMLツールがあり、たとえば、DataRobotは特徴量の適切な自動選択を行うことができます。  

    当然ながら、上記のDataRobotグラフから、さまざまなRespiratoryRate値とBloodPressure値がICU入室に最も関連性の高い特徴量であることがわかります。    

    特徴量の選択 - 最終選択
    この記事では、簡単な実験を行い、LightGBMによる特徴量の選択が実際にほんの少し良い結果を出したことに気づいたので、この選択方法のみを使用することにします。   

    df_selected_columns = embeded_lgb_feature  # better than ensembled selectiondataS = pd.concat([idf[df_selected_columns],idf['ICU'], df_cat['WINDOW']],1)
    dataS.ICU.value_counts()
    print(dataS.shape)
    (1925, 58)

    58個の特徴量が選択されていることがわかります。多すぎず、少なからず、この特定のシングルターゲット二項分類問題には適切な量だと言えます。 

    データの不均衡

    plt.figure(figsize=(10,5))
    count = sns.countplot(x = "ICU",data=data)
    count.set_xticklabels(["Not Admitted","Admitted"])
    plt.xlabel("ICU Admission")
    plt.ylabel("Patient Count")
    plt.show()

    これはデータが不均衡であることを示し、ICUに入室したというレコードは26%のみです。 これは結果に影響を及ぼすため、SMOTEなどの通常のデータ均衡化アプローチを考えられます。

    ここでは、他のあらゆる種類のEDAを試し、それに応じてさまざまなデータ分布を理解することができます。 

    基本的なLRトレーニングを実行する

    Kaggleサイトには、独自の特徴量カラムの選択に基づいて素早く実行できる優れた簡易トレーニングノートブックがいくつかあります。  とりあえず、トレーニングパイプラインのLR分類器をさっと実行することから始めましょう。 

    data2 = pd.concat([idf[df_selected_columns],idf['ICU'], df_cat['WINDOW']],1)   
    data2.AGE_ABOVE65 = data2.AGE_ABOVE65.astype(int)
    data2.ICU = data2.ICU.astype(int)
    X2 = data2.drop("ICU",1)
    y2 = data2.ICU
    
    from sklearn.preprocessing import LabelEncoder
    label_encoder = LabelEncoder()
    X2.WINDOW = label_encoder.fit_transform(np.array(X2["WINDOW"].astype(str)).reshape((-1,)))
    
    confusion_matrix2 = pd.crosstab(y2_test, y2_hat, rownames=['Actual'], colnames=['Predicted'])
    sns.heatmap(confusion_matrix2, annot=True, fmt = 'g', cmap = 'Reds') print("ORIGINAL")
    print(classification_report(y_test, y_hat))
    print("AUC = ",roc_auc_score(y_test, y_hat),'\n\n')
    print("LABEL ENCODING")
    print(classification_report(y2_test, y2_hat))
    print("AUC = ",roc_auc_score(y2_test, y2_hat))
    y2hat_probs = LR.predict_proba(X2_test)
    y2hat_probs = y2hat_probs[:, 1] fpr2, tpr2, _ = roc_curve(y2_test, y2hat_probs) plt.figure(figsize=(10,7))
    plt.plot([0, 1], [0, 1], 'k--')
    plt.plot(fpr, tpr, label="Base")
    plt.plot(fpr2,tpr2,label="Label Encoded")
    plt.xlabel('False positive rate')
    plt.ylabel('True positive rate')
    plt.title('ROC curve')
    plt.legend(loc="best")
    plt.show()
    
    
    ORIGINAL
                  precision    recall  f1-score   support
               0       0.88      0.94      0.91       171
               1       0.76      0.57      0.65        54
        accuracy                           0.85       225
       macro avg       0.82      0.76      0.78       225
    weighted avg       0.85      0.85      0.85       225
    AUC =  0.7577972709551657 
    LABEL ENCODING
                  precision    recall  f1-score   support
               0       0.88      0.93      0.90       171
               1       0.73      0.59      0.65        54
        accuracy                           0.85       225
       macro avg       0.80      0.76      0.78       225
    weighted avg       0.84      0.85      0.84       225
    AUC =  0.7612085769980507

            

    85%の精度でAUC 76%を達成しているようですが、ICU入院のRecallは59%のみで、偽陰性が多すぎるようです。 それは確かに理想的ではありません。患者のレコードに対して実際のICUリスクを見落とすわけにはいきません。 そこで、すべての後続のタスクでは、うまく行けばある程度均衡のとれた全体的精度でFNを下げてRecallレートを引き上げる方法に目標を定めることにします。

    前のセクションでは、不均衡データについて触れました。最初の直感は、テストセットを層別化(Stratify)し、SMOTEしてより均衡のとれたデータセットを作成することです。

    #テストデータを層別化し、TrainデータとTestデータの比率が同じ1:0になるようにする
    X3_train,X3_test,y3_train,y3_test = train_test_split(X2,y2,test_size=225/1925,random_state=42, stratify = y2, shuffle = True) &lt;span> &lt;/span>
    # train and predict
    LR.fit(X3_train,y3_train)
    y3_hat = LR.predict(X3_test)
    
    #データをSMOTEし、ICU 1:0を均衡のとれた分布にする
    from imblearn.over_sampling import SMOTE sm = SMOTE(random_state = 42)
    X_train_res, y_train_res = sm.fit_sample(X3_train,y3_train.ravel())
    LR.fit(X_train_res, y_train_res)
    y_res_hat = LR.predict(X3_test)
    
    #混同行列などをもう一度描画する
    confusion_matrix3 = pd.crosstab(y3_test, y_res_hat, rownames=['Actual'], colnames=['Predicted'])
    sns.heatmap(confusion_matrix3, annot=True, fmt = 'g', cmap="YlOrBr") 
    print("LABEL ENCODING + STRATIFY")
    print(classification_report(y3_test, y3_hat))
    print("AUC = ",roc_auc_score(y3_test, y3_hat),'\n\n') 
    print("SMOTE")
    print(classification_report(y3_test, y_res_hat))
    print("AUC = ",roc_auc_score(y3_test, y_res_hat)) 
    y_res_hat_probs = LR.predict_proba(X3_test)
    y_res_hat_probs = y_res_hat_probs[:, 1] 
    fpr_res, tpr_res, _ = roc_curve(y3_test, y_res_hat_probs) plt.figure(figsize=(10,10))
    
    #前のようにROC曲線をプロットする

     

    LABEL ENCODING + STRATIFY
                  precision    recall  f1-score   support
               0       0.87      0.99      0.92       165
               1       0.95      0.58      0.72        60
        accuracy                           0.88       225
       macro avg       0.91      0.79      0.82       225
    weighted avg       0.89      0.88      0.87       225
    AUC =  0.7856060606060606 
    SMOTE
                  precision    recall  f1-score   support
               0       0.91      0.88      0.89       165
               1       0.69      0.75      0.72        60
        accuracy                           0.84       225
       macro avg       0.80      0.81      0.81       225
    weighted avg       0.85      0.84      0.85       225
    AUC =  0.8143939393939393

                

    したがって、データのSTRATIFY処理とSMOTE処理はRecallを0.59から 0.75、全体的な精度を0.84に改善しているようです。 

    従来型MLのデータ処理の大半がいつも通り完了したので、このケースの最善のモデルが何であるかを知りたいと思います。それらはもっと優れているのでしょうか。また、比較的包括的な比較を試みることができるでしょうか。

    さまざまなモデルのトレーニング比較を実行する 

    一般的に使用されているMLアルゴリズムを評価し、箱ひげ図によって、結果の比較ダッシュボードを生成してみましょう。

    # アルゴリズムを比較
    from matplotlib import pyplot
    from sklearn.model_selection import train_test_split
    from sklearn.model_selection import cross_val_score
    from sklearn.model_selection import StratifiedKFold
    from sklearn.linear_model import LogisticRegression
    from sklearn.tree import DecisionTreeClassifier
    from sklearn.neighbors import KNeighborsClassifier
    from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
    from sklearn.naive_bayes import GaussianNB
    from sklearn.svm import SVC
    #ランダムフォレストモデルをインポートする
    from sklearn.ensemble import RandomForestClassifier
    from xgboost import XGBClassifier# アルゴリズムを合わせてリストする
    models = []
    models.append(('LR', &lt;strong>LogisticRegression&lt;/strong>(solver='liblinear', multi_class='ovr')))
    models.append(('LDA', LinearDiscriminantAnalysis()))
    models.append(('KNN', &lt;strong>KNeighborsClassifier&lt;/strong>()))
    models.append(('CART', &lt;strong>DecisionTreeClassifier&lt;/strong>()))
    models.append(('NB', &lt;strong>GaussianNB&lt;/strong>()))
    models.append(('SVM', &lt;strong>SVC&lt;/strong>(gamma='auto')))
    models.append(('RF', &lt;strong>RandomForestClassifier&lt;/strong>(n_estimators=100)))
    models.append(('XGB', &lt;strong>XGBClassifier&lt;/strong>())) #clf = XGBClassifier()
    # 代わりに各モデルを評価する
    results = []
    names = []
    for name, model in models:
        kfold = StratifiedKFold(n_splits=10, random_state=1)
        cv_results = cross_val_score(model, X_train_res, y_train_res, cv=kfold, scoring='f1')  ## accuracy, precision,recall 
        results.append(cv_results)
        names.append(name)
        print('%s: %f (%f)' % (name, cv_results.mean(), cv_results.std()))# すべてのモデルのパフォーマンスを比較する。 質問 - それに関する統合アイテムを見たいですか?
    pyplot.figure(4, figsize=(12, 8))
    pyplot.boxplot(results, labels=names)
    pyplot.title('Algorithm Comparison')
    pyplot.show()
    LR: 0.805390 (0.021905) LDA: 0.803804 (0.027671) KNN: 0.841824 (0.032945) CART: 0.845596 (0.053828)
    NB: 0.622540 (0.060390) SVM: 0.793754 (0.023050) RF: 0.896222 (0.033732) XGB: 0.907529 (0.040693)

    上記は、XGB分類子とランダムフォレスト分類子に他のモデルよりも優れたF1スコアがあるように見えます。 

    同じセットの正規化されたテストデータで実際のテスト結果を比較して見ましょう。

    import time
    from pandas import read_csv
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import classification_report
    from sklearn.metrics import confusion_matrix
    from sklearn.metrics import accuracy_score
    from sklearn.svm import SVCfor name, model in models:
        print(name + ':\n\r')
        start = time.clock()
        model.fit(X_train_res, y_train_res)
        print("Train time for ", model, " ", time.clock() - start)
        predictions = model.predict(X3_test) #(X_validation)
        # Evaluate predictions
        print(accuracy_score(y3_test, predictions))  # Y_validation
        print(confusion_matrix(y3_test, predictions))
        print(classification_report(y3_test, predictions))
    LR:
    Train time for  LogisticRegression(multi_class='ovr', solver='liblinear')   0.02814499999999498
    0.8444444444444444
    [[145  20]
     [ 15  45]]
                  precision    recall  f1-score   support
               0       0.91      0.88      0.89       165
               1       0.69      0.75      0.72        60
        accuracy                           0.84       225
       macro avg       0.80      0.81      0.81       225
    weighted avg       0.85      0.84      0.85       225
    
    LDA:
    Train time for  LinearDiscriminantAnalysis()   0.2280070000000194
    0.8488888888888889
    [[147  18]
     [ 16  44]]
                  precision    recall  f1-score   support
               0       0.90      0.89      0.90       165
               1       0.71      0.73      0.72        60
        accuracy                           0.85       225
       macro avg       0.81      0.81      0.81       225
    weighted avg       0.85      0.85      0.85       225
    
    KNN:
    Train time for  KNeighborsClassifier()   0.13023699999999394
    0.8355555555555556
    [[145  20]
     [ 17  43]]
                  precision    recall  f1-score   support
               0       0.90      0.88      0.89       165
               1       0.68      0.72      0.70        60
        accuracy                           0.84       225
       macro avg       0.79      0.80      0.79       225
    weighted avg       0.84      0.84      0.84       225
    
    CART:
    Train time for  DecisionTreeClassifier()   0.32616000000001577
    0.8266666666666667
    [[147  18]
     [ 21  39]]
                  precision    recall  f1-score   support
               0       0.88      0.89      0.88       165
               1       0.68      0.65      0.67        60
        accuracy                           0.83       225
       macro avg       0.78      0.77      0.77       225
    weighted avg       0.82      0.83      0.83       225
    
    NB:
    Train time for  GaussianNB()   0.0034229999999979555
    0.8355555555555556
    [[154  11]
     [ 26  34]]
                  precision    recall  f1-score   support
               0       0.86      0.93      0.89       165
               1       0.76      0.57      0.65        60
        accuracy                           0.84       225
       macro avg       0.81      0.75      0.77       225
    weighted avg       0.83      0.84      0.83       225
    
    SVM:
    Train time for  SVC(gamma='auto')   0.3596520000000112
    0.8977777777777778
    [[157   8]
     [ 15  45]]
                  precision    recall  f1-score   support
               0       0.91      0.95      0.93       165
               1       0.85      0.75      0.80        60
        accuracy                           0.90       225
       macro avg       0.88      0.85      0.86       225
    weighted avg       0.90      0.90      0.90       225
    
    RF:
    Train time for  RandomForestClassifier()   0.50123099999999
    0.9066666666666666
    [[158   7]
     [ 14  46]]
                  precision    recall  f1-score   support
               0       0.92      0.96      0.94       165
               1       0.87      0.77      0.81        60
        accuracy                           0.91       225
       macro avg       0.89      0.86      0.88       225
    weighted avg       0.91      0.91      0.90       225
    
    XGB:
    Train time for  XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
                  colsample_bynode=1, colsample_bytree=1, gamma=0, gpu_id=-1,
                  importance_type='gain', interaction_constraints='',
                  learning_rate=0.300000012, max_delta_step=0, max_depth=6,
                  min_child_weight=1, missing=nan, monotone_constraints='()',
                  n_estimators=100, n_jobs=0, num_parallel_tree=1, random_state=0,
                  reg_alpha=0, reg_lambda=1, scale_pos_weight=1, subsample=1,
                  tree_method='exact', validate_parameters=1, verbosity=None)   1.649520999999993
    0.8844444444444445
    [[155  10]
     [ 16  44]]
                  precision    recall  f1-score   support
               0       0.91      0.94      0.92       165
               1       0.81      0.73      0.77        60
        accuracy                           0.88       225
       macro avg       0.86      0.84      0.85       225
    weighted avg       0.88      0.88      0.88       225

    RFが実際にXGBよりも優れているという結果が出たようです。 XGBが何らかの形で過学習している可能性を示唆しているかもしれません。 RFCの結果はLRよりもわずかに改善されています。

    「グリッド検索によるパラメーターチューニング」をさらに行って、選択されたモデルを実行する

    では、ランダムフォレスト分類子がここで選ばれたモデルと仮定しましょう。  このモデルにさらにグリッド検索を実行して、結果のパフォーマンスをわずかにでも向上させられるかを確認することができます。 

    ここでの目標は、患者が遭遇する潜在的なICUリスクの偽陰性を最小限に抑えることで、このケースのRecallを最適化することに変わりはありませんので、「recall_score」を使用して、グリッド検索を再適合させます。 繰り返しますが、上記のテストセットは常に2915件のレコードの約12%に設定されていたため、ここでも10倍の交差検定を使用します。

    from sklearn.model_selection import GridSearchCV
    # ランダム検索の結果に基づいてパラメーターグリッドを作成する
    
    param_grid = {'bootstrap': [True],
     'ccp_alpha': [0.0],
     'class_weight': [None],
     'criterion': ['gini', 'entropy'],
     'max_depth': [None],
     'max_features': ['auto', 'log2'],             
     'max_leaf_nodes': [None],
     'max_samples': [None],
     'min_impurity_decrease': [0.0],
     'min_impurity_split': [None],
     'min_samples_leaf': [1, 2, 4],
     'min_samples_split': [2, 4],
     'min_weight_fraction_leaf': [0.0],
     'n_estimators': [100, 125],
     #'n_jobs': [None],
     'oob_score': [False],
     'random_state': [None],
     #'verbose': 0,
     'warm_start': [False]
    }#混同行列でファインチューニングする 
    from sklearn.metrics import roc_curve, precision_recall_curve, auc, make_scorer, recall_score, accuracy_score, precision_score, confusion_matrix
    scorers = {
        'recall_score': make_scorer(recall_score),
        'precision_score': make_scorer(precision_score),
        'accuracy_score': make_scorer(accuracy_score)
    }# ベースモデルを作成する
    rfc = RandomForestClassifier()
    # Instantiate the grid search model
    grid_search = GridSearchCV(estimator = rfc, param_grid = param_grid, 
                               scoring=scorers, refit='recall_score', 
                               cv = 10, n_jobs = -1, verbose = 2)train_features = X_train_resgrid_search.fit(train_features, train_labels)
    rf_best_grid = grid_search.best_estimator_rf_best_grid.fit(train_features, train_labels)
    rf_predictions = rf_best_grid.predict(X3_test) 
    print(accuracy_score(y3_test, rf_predictions))  
    print(confusion_matrix(y3_test, rf_predictions))
    print(classification_report(y3_test, rf_predictions))
    0.92
    [[ 46  14]
     [  4 161]]
                  precision    recall  f1-score   support
               0       0.92      0.77      0.84        60
               1       0.92      0.98      0.95       165
        accuracy                           0.92       225
       macro avg       0.92      0.87      0.89       225
    weighted avg       0.92      0.92      0.92       225

    グリッド検索は、FNを同じに保ちながら、実際に全体的な精度を少し上げることができるという結果が得られました。 

    AUC比較もプロットしてみましょう。

    confusion_matrix4 = pd.crosstab(y3_test, rf_predictions, rownames=['Actual'], colnames=['Predicted'])
    sns.heatmap(confusion_matrix4, annot=True, fmt = 'g', cmap="YlOrBr")print("LABEL ENCODING + STRATIFY")
    print(classification_report(y3_test, 1-y3_hat))
    print("AUC = ",roc_auc_score(y3_test, 1-y3_hat),'\n\n')print("SMOTE")
    print(classification_report(y3_test, 1-y_res_hat))
    print("AUC = ",roc_auc_score(y3_test, 1-y_res_hat), '\n\n')print("SMOTE + LBG Selected Weights + RF Grid Search")
    print(classification_report(y3_test, rf_predictions))
    print("AUC = ",roc_auc_score(y3_test, rf_predictions), '\n\n\n')y_res_hat_probs = LR.predict_proba(X3_test)
    y_res_hat_probs = y_res_hat_probs[:, 1]predictions_rf_probs = rf_best_grid.predict_proba(X3_test) #(X_validation)
    predictions_rf_probs = predictions_rf_probs[:, 1]fpr_res, tpr_res, _ = roc_curve(y3_test, 1-y_res_hat_probs)
    fpr_rf_res, tpr_rf_res, _ = roc_curve(y3_test, predictions_rf_probs)plt.figure(figsize=(10,10))
    plt.plot([0, 1], [0, 1], 'k--')
    plt.plot(fpr, tpr, label="Base")
    plt.plot(fpr2,tpr2,label="Label Encoded")
    plt.plot(fpr3,tpr3,label="Stratify")
    plt.plot(fpr_res,tpr_res,label="SMOTE")
    plt.plot(fpr_rf_res,tpr_rf_res,label="SMOTE + RF GRID")
    plt.xlabel('False positive rate')
    plt.ylabel('True positive rate')
    plt.title('ROC curve')
    plt.legend(loc="best")
    plt.show()
    LABEL ENCODING + STRATIFY
                  precision    recall  f1-score   support
               0       0.95      0.58      0.72        60
               1       0.87      0.99      0.92       165
        accuracy                           0.88       225
       macro avg       0.91      0.79      0.82       225
    weighted avg       0.89      0.88      0.87       225
    AUC =  0.7856060606060606 
    
    SMOTE
                  precision    recall  f1-score   support
               0       0.69      0.75      0.72        60
               1       0.91      0.88      0.89       165
        accuracy                           0.84       225
       macro avg       0.80      0.81      0.81       225
    weighted avg       0.85      0.84      0.85       225
    AUC =  0.8143939393939394 
    
    SMOTE + LBG Selected Weights + RF Grid Search
                  precision    recall  f1-score   support
               0       0.92      0.77      0.84        60
               1       0.92      0.98      0.95       165
        accuracy                           0.92       225
       macro avg       0.92      0.87      0.89       225
    weighted avg       0.92      0.92      0.92       225
    AUC =  0.8712121212121211

         

    アルゴリズム比較とさらなるグリッド検索の結果、AUCを78%から87%に引き上げることができ、全体の精度は92%、Recallは77%という結果を得られました。

    「従来型ML」アプローチのまとめ

    では、実際のところこの結果はどうなのでしょうか。 従来型MLアルゴリズムを使った基本的な手動プロセスとしては十分と言えます。  Kaggleコンペティションではどのように見えるでしょうか。 まぁ、リーダーボードには上がらないでしょう。 DataRobotの現在のAutoMLサービスから生のデータセットを実行したところ、最良の結果は、上位43個のモデルを比較したうち、「教師なし学習特徴によるXGBツリー分類子」モデルで、約90%以上相当のAUCを達成すると主張するでしょう。 本当にKaggleに参戦したいのであれば、これが私たちが狙いとする実用的なモデルと言えるでしょう。  最高の結果とモデルの上位リストもGitHubに添付しておきます。 最終的には、ケアサイトに特化した実際のケースについて言えば、この記事の「データとタスク」セクションで述べたように、ある程度カスタマイズされたディープラーニングアプローチにも焦点を当てる必要があると感じています。 もちろん、実際のケースでは、質の高いデータカラムをどこで収集するのかという手前の問題もあるでしょうが。

    IntegratedMLアプローチとは

    上記では、いわゆる従来型MLと呼ばれるプロセスを説明しました。通常は、データEDA、特徴量エンジニアリング、特徴量の選択、モデルの選択、およびグリッド検索などによるパフォーマンスの最適化が含まれるプロセスです。 これがこのタスクにおいて、今のところ思いつく最も単純なアプローチです。まだモデルのデプロイやサービス管理のライフサイクルについては触れていません。これについては次の記事で、Flask/FlasAPI/IRISを利用して、この基本MLモデルをCovid-19X線画像デモサービススタックにデプロイする方法を探りながら、説明したいと思います。

    IRISにはIntegratedMLが組み込まれており、強力なAutoMLオプションの洗練されたSQLラッパーとして用意されています。 パート2では、ビジネスのメリットとして同等のML結果を引き出しながら、特徴量の選択、モデルの選択、パフォーマンスの最適化などを行わずに済むように、上記のタスクを大幅に単純化したプロセスで達成する方法を調べます。

    以上です。同じデータでintegratedMLのクイックランをこの記事に追加するには、10分メモとしては長すぎてしまうため、次のパート2の記事で話すことにします。 

    0
    0 314
    記事 Toshihiko Minamoto · 11月 30, 2021 16m read

    キーワード: COVID-19、医用画像、ディープラーニング、PACSビューア、HealthShare。

    目的

    私たちは皆、この前例のないCovid-19パンデミックに悩まされています。 現場のお客様をあらゆる手段でサポートする一方で、今日のAI技術を活用して、Covid-19に立ち向かうさまざまな前線も見てきました。 

    昨年、私はディープラーニングのデモ環境について少し触れたことがあります。 この長いイースターの週末中に、実際の画像を扱ってみてはどうでしょうか。Covid-19に感染した胸部X線画像データセットに対して簡単な分類を行うディープラーニングモデルをテスト実行し、迅速な「AIトリアージ」や「放射線科医の支援」の目的で、X線画像やCT用のツールがdockerなどを介してクラウドにどれほど素早くデプロイされるのかを確認してみましょう。     

    これは、10分程度の簡易メモです。学習過程において、最も単純なアプローチでハンズオン経験を得られることを願っています。   

    範囲

    このデモ環境では、次のコンポーネントが使用されます。 これまで見てきた中で最も単純な形式です。

    • 3タイプの小さな匿名加工オープンデータセット: Covid-19感染胸部、細菌性肺炎胸部、正常な胸部。
    • 1セットのディープラーニングモデル(X線胸部画像分類用のInception V3モデル)
    • JupyterノートブックによるTensorFlow 1.13.2コンテナ
    • GPU用のNvidia-Docker2コンテナ
    • Nvidia T4 GPU搭載AWS Ubuntu 16.04 VM(事前トレーニング済みモデルの再トレーニングを行わない場合はノートパソコンのGPUで十分です)

    および

    • 「AI支援CT検出」のデモコンテナ
    • サードパーティのOpen PACS Viewerのデモコンテナ
    • HealthShare Clinical Viewerのデモインスタンス

    以下は、このデモの範囲では説明されていません。

    • PyTorchの人気が高まっています(次の機会に利用します)
    • デモ環境では、TensorFlow 2.0の実行速度がはるかに低下します(そのためバージョン1.13に戻します) 
    • AutoMLなどのマルチモデルアンサンブル(実際に人気が高まっていますが、この小さなデータセットでは昔ながらのシングルモデルで十分です)
    • 実際のサイトから得るX線およびCTデータ。 

    免責事項

    このデモは、この特定の分野における臨床試験についてではなく、技術的なアプローチに関するものです。 CTおよびX線などの証拠に基づくCovid-19診断は現在ではオンラインで広く入手可能であり、肯定的なレビューもあれば、否定的なレビューもあります。また、このパンデミックにおいて、国や文化における役割が実際に異なります。 また、この記事のコンテンツとレイアウトは、必要に応じて変更される場合があります。   このコンテンツは「開発者」としての純粋な個人的見解です。

    データ

    このテストの元の画像は、Joseph Paul Cohenによって一般公開されているCovid-19 Lung X-Ray setに含まれるものと、オープン形式のKaggle Chest X-Ray setsからAdrian YuがGradientCrescentリポジトリの小さなテストセットに集めたの正常な胸部画像です。 また、皆さんが使用できるように、簡単なテスト用のテストデータをこちらにアップロードしました。 これまでのところ、以下の小さなトレーニングセットが含まれます。 

    • Covid-19に感染した胸部画像 60点 
    • 正常な胸部画像 70点
    • 細菌性肺炎に感染した胸部画像 70点

    テスト

    以下のテストでは、独自のフレーバーに合わせてわずかに変更したものを実行しました。

    • ベースのInception V3モデルとその上の2つのCNNレイヤー
    • 再トレーニング用に基盤のInceptionレイヤーの非凍結重みを使った転移学習(ノートパソコンのGPUでは、事前トレーニング済みのInceptionレイヤーを凍結するだけです)
    • これまでに収集した小さなデータセットを補うためのわずかなデータ拡張
    • バイナリの代わりに3つのカテゴリを使用: Covid-19、正常、細菌性(またはウイルス性)肺炎(これらの3つのクラスを使用した理由は後で説明します)
    • 後のステップで使えるテンプレートとして、基本的な3クラス混同行列を計算  

    注意: ほかの一般的なCNNベースのモデル(VGG16やResNet50など)ではなく、Inception V3を選択した理由は特にありません。 他にもモデルがありますが、最近、骨折データセットのデモ実行に使用したため、ここではそれを再利用しているだけです。  以下のJupyterノートブックスクリプトを実行し直す際には、お好きなモデルを使用してください。

    この記事には、Jupyterノートブックも添付しました。 以下に簡単な説明も示しています。

    1. 必要なライブラリをインポートする

    # import the necessary packages
    from tensorflow.keras.layers import AveragePooling2D, Dropout, Flatten, Dense, Input
    from tensorflow.keras.models import Model
    from tensorflow.keras.optimizers import Adam
    from tensorflow.keras.utils import to_categorical
    from tensorflow.keras import optimizers, models, layers
    from tensorflow.keras.applications.inception_v3 import InceptionV3
    from tensorflow.keras.applications.resnet50 import ResNet50
    
    from tensorflow.keras.preprocessing.image import ImageDataGenerator
    from sklearn.preprocessing import LabelEncoder, OneHotEncoder
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import classification_report, confusion_matrix
    from imutils import paths
    import matplotlib.pyplot as plt
    import numpy as np
    import cv2
    import os

    2. ここに提供されているサンプルの画像ファイルを読み込む

    # set learning rate, epochs and batch size
    INIT_LR = 1e-5    # この値は選択したモデルに固有の値です: Inception、VGG、ResNetなど。
    EPOCHS = 50 
    BS = 8
    
    print("Loading images...")
    imagePath = "./Covid_M/all/train"  # 同じ画像のローカルパスに変更してください
    imagePaths = list(paths.list_images(imagePath))
    
    data = []
    labels = []
    
    # 指定されたパスにあるすべてのX線画像を読み取り、サイズを256x256に変更します
    for imagePath in imagePaths:
        label = imagePath.split(os.path.sep)[-2]
        image = cv2.imread(imagePath)
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        image = cv2.resize(image, (256, 256))
        data.append(image)
        labels.append(label)
        
    #ピクセル値を0.0~1.0の実数に正規化します
    data = np.array(data) / 255.0
    labels = np.array(labels)
    
    # マルチクラスのラベル付けを行うためにワンホットエンコーディングを実行します
    label_encoder = LabelEncoder()
    integer_encoded = label_encoder.fit_transform(labels)
    labels = to_categorical(integer_encoded)
    
    print("... ... ", len(data), "images loaded in multiple classes:")
    print(label_encoder.classes_)
    Loading images...
    ... ...  200 images loaded in 3x classes:
    ['covid' 'normal' 'pneumonia_bac']

    3. 基本的なデータ拡張を追加し、モデルを再構成してトレーニングする 

    # データとトレーニング用と検証用に分割します。
    (trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.20, stratify=labels, random_state=42)
    
    # 単純な拡張を追加します。 注意: このケースでは、拡張が多すぎるとうまく行きません。テストの過程でわかりました。 
    trainAug = ImageDataGenerator(rotation_range=15, fill_mode="nearest")
    
    #事前トレーニング済み「ImageNet」の重みの転移学習でInceptionV3モデルを使用します。
    #注意: VGG16またはResNetを選択した場合は、最上部で初期の学習率をリセットする必要がある場合があります。
    baseModel = InceptionV3(weights="imagenet", include_top=False, input_tensor=Input(shape=(256, 256, 3)))
    #baseModel = VGG16(weights="imagenet", include_top=False, input_tensor=Input(shape=(256, 256, 3)))
    #baseModel = ResNet50(weights="imagenet", include_top=False, input_tensor=Input(shape=(256, 256, 3)))
    #Inception V3モデルの上にカスタムCNNレイヤーを2つ追加します。 
    headModel = baseModel.output
    headModel = AveragePooling2D(pool_size=(4, 4))(headModel)
    headModel = Flatten(name="flatten")(headModel)
    headModel = Dense(64, activation="relu")(headModel)
    headModel = Dropout(0.5)(headModel)
    headModel = Dense(3, activation="softmax")(headModel)
    
    # 最終モデルを構成します
    model = Model(inputs=baseModel.input, outputs=headModel)
    
    # Navidia T4 GPUを使用しているので、再トレーニングを行うために、事前トレーニング済みのInception「ImageNet」重みの凍結を解除します
    #baseModel.layersのレイヤーの場合:
    #    layer.trainable = False
    
    print("Compiling model...")
    opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
    model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])
    
    # 上記で事前トレーニング済みの重みの凍結を解除したため、全モデルをトレーニングします。
    print("Training the full stack model...")
    H = model.fit_generator( trainAug.flow(trainX, trainY, batch_size=BS), steps_per_epoch=len(trainX) // BS,
                             validation_data=(testX, testY), validation_steps=len(testX) // BS, epochs=EPOCHS)
    ... ... 
    Compiling model...
    Training the full stack model...
    ... ...
    Use tf.cast instead.
    Epoch 1/50
    40/40 [==============================] - 1s 33ms/sample - loss: 1.1898 - acc: 0.3000
    20/20 [==============================] - 16s 800ms/step - loss: 1.1971 - acc: 0.3812 - val_loss: 1.1898 - val_acc: 0.3000
    Epoch 2/50
    40/40 [==============================] - 0s 6ms/sample - loss: 1.1483 - acc: 0.3750
    20/20 [==============================] - 3s 143ms/step - loss: 1.0693 - acc: 0.4688 - val_loss: 1.1483 - val_acc: 0.3750
    Epoch 3/50
    ... ...
    ... ...
    Epoch 49/50
    40/40 [==============================] - 0s 5ms/sample - loss: 0.1020 - acc: 0.9500
    20/20 [==============================] - 3s 148ms/step - loss: 0.0680 - acc: 0.9875 - val_loss: 0.1020 - val_acc: 0.9500
    Epoch 50/50
    40/40 [==============================] - 0s 6ms/sample - loss: 0.0892 - acc: 0.9750
    20/20 [==============================] - 3s 148ms/step - loss: 0.0751 - acc: 0.9812 - val_loss: 0.0892 - val_acc: 0.9750

     

    4. 検証結果用に混同行列をプロットする

    print("Evaluating the trained model ...")
    predIdxs = model.predict(testX, batch_size=BS)
    
    predIdxs = np.argmax(predIdxs, axis=1)
    
    print(classification_report(testY.argmax(axis=1), predIdxs, target_names=label_encoder.classes_))
    
    # 基本的な混同行列を計算します
    cm = confusion_matrix(testY.argmax(axis=1), predIdxs)
    total = sum(sum(cm))
    acc = (cm[0, 0] + cm[1, 1] + cm[2, 2]) / total
    sensitivity = cm[0, 0] / (cm[0, 0] + cm[0, 1] + cm[0, 2])
    specificity = (cm[1, 1] + cm[1, 2] + cm[2, 1] + cm[2, 2]) / (cm[1, 0] + cm[1, 1] + cm[1, 2] + cm[2, 0] + cm[2, 1] + cm[2, 2])
    
    # 混同行列、精度、感度、特異度を示します
    print(cm)
    print("acc: {:.4f}".format(acc))
    print("sensitivity: {:.4f}".format(sensitivity))
    print("specificity: {:.4f}".format(specificity))
    
    # トレーニング損失と精度をプロットします
    N = EPOCHS 
    plt.style.use("ggplot")
    plt.figure()
    plt.plot(np.arange(0, N), H.history["loss"], label="train_loss")
    plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")
    plt.plot(np.arange(0, N), H.history["acc"], label="train_acc")
    plt.plot(np.arange(0, N), H.history["val_acc"], label="val_acc")
    plt.title("Training Loss and Accuracy on COVID-19 Dataset")
    plt.xlabel("Epoch #")
    plt.ylabel("Loss/Accuracy")
    plt.legend(loc="lower left")
    plt.savefig("./Covid19/s-class-plot.png")

    上の図から、小さなデータセットと5分未満の簡易トレーニングでも「転移学習」のメリットにより、結果はそれほど悪くないことがわかります。12点すべてのCovid-19に感染した胸部画像は正しく分類されており、誤って「細菌性肺炎」の胸部画像として分類された正常な胸部画像は、合計40点のうち1点のみです。 

     

    5. 実際のX線画像をテストするための混同行列をプロットする

    では、さらにもう一歩飛躍してみましょう。簡単にトレーニングされたこの分類器がどれほど効果的であるかをテストするために、実際のX線画像を送信してみます。 

    上記のトレーニングや検証セットで使用していない27点のX線画像をモデルにアップロードしました。

    Covid-19に感染した胸部画像 9点と正常な胸部画像 9点と細菌性肺炎に感染した胸部画像 9点です。  (これらの画像は、この記事にも添付されています。)

    ステップ2のコードを1行だけ変更し、テスト画像が確実に異なるパスから読み込まれるようにしました。

    ...
    imagePathTest = "./Covid_M/all/test"
    ...

    次に上記のトレーニング済みのモデルを使用して予測します。 

    predTest = model.predict(dataTest, batch_size=BS)
    print(predTest)
    predClasses = predTest.argmax(axis=-1)
    print(predClasses)
    ...

    最後に、ステップ5のようにして、混同行列を再計算します。

    testX = dataTest
    testY = labelsTest
    ... ...

    実際のテスト結果が得られました。

     またしても、トレーニング済みモデルは、すべてのCovid-19に感染した胸部画像を正しく分類できるようです。 こんなに小さなデータセットにしては、それほど悪くない出来です。 

    6. さらなる監察結果

    このイースターの週末に、さまざまなデータセットを試し、Covid-19に感染した胸部画像には明確な特徴があるように見えました。AI分類器の観点では、通常の細菌性またはウイルス性(インフルエンザ)感染症の影響を受けた胸部画像と区別することは比較的簡単です。

    また、いくつかの簡単なテストから、細菌性とウイルス性(一般的なインフルエンザ)の胸部画像を区別するのは非常に困難であることに気づきました。  時間があれば、ほかのKaggle挑戦者がおそらくあの状況で行うように、クラスタアンサンブルを使用して、その違いを追跡しなければならないでしょう。  

    では、臨床の観点では、上記の結果は本当に正しいのでしょうか? Covid-19に感染した胸部にはX線で本当に明確な特徴が見られるのでしょうか? あまり確信が持てません。 実際の胸部放射線科医に意見を聞かなくてはいけないでしょう。 今のところは、現在のデータセットは結論を出すには小さすぎると思います。  

    : 実際のサイトのX線画像をもっと集めて、xgsboot、AutoML、または新しいIRIS IntegratedMLワークベンチを使って、深く調べたいと思います。  さらに、医師やA&Eトリアージ向けに、Covid-19に感染した胸部画像を、その深刻度に応じてレベル1、レベル2、およびレベル3のようにさらに分類できるはずです。

    とにかく話を戻すと、データセットと上記のJupyterノートブックを添付しました

    デプロイメント

    上記では、この「医用画像」分野における簡単なセットアップについて、わりと単純な出発点に触れました。 このCovid-19フロントは、実際には、過去1年の週末や長期休暇中に調べたもののうち、3つ目の試行となります。 他には、「AI支援骨折検出」システムや「AI支援眼科網膜診断」システムなどを調べました。 

    上記のモデルは、今のところは単純すぎて試すほどでもないかもしれませんが、一般的な疑問はすぐにでも避けて通れないでしょう。ある種の「AIサービス」はどのようにデプロイすればよいのでしょうか? 

    テクノロジースタックとサービスのライフサイクルに関わることですが、どのような問題を解決しようとしているのか、どういった実際の価値が得られるのかといった実際の「ユースケース」によっても異なります。  答えはテクノロジーほど明確ではないこともあります。

    イギリスのRCR(王立放射線学者会)の原案では、「放射線技師のAI支援」と「A&EまたはプライマリケアにおけるAIトリアージ」という2つの単純なユースケースが提案されました。 正直なところ現時点では2つ目の「AIトリアージ」に同意しており、個人的には、この方がより高い価値をもたらすのではないかと思っています。  また、幸いにも、今日の開発者はクラウド、Docker、AI、そしてもちろんInterSystemsのHealthShareにより、これまで以上の力を使ってこの種のケースを解決することができます。 

    たとえば、以下のスクリーンキャプチャは実際にAWSにホストされたエンタープライズ級の「AI支援によるCovid-19感染胸部のCT検出」サービスを示しており、デモの目的で、いかにしてこのサービスを直接HealthShare Clinical Viewerに組み込むかを説明しています。 X線撮影と同様に、DICOMに設定されたCTも迅速な「AIトリアージ」のユースケース用に毎日24時間稼働しながら、このオープンPACS Viewerに直接アップロードまたは送信し、「AI診断」をクリックするだけで、トレーニング済みモデルに基づいて定量化されたCovid-19感染確率の判定を提供することができます。 X線画像分類などのモデルは、最前線に立つ医師を支援できるよう、同一患者のコンテキストで既存のPACSビューアの上にデプロイし、同じ方法で呼び出すことができます。  

    謝辞  

    繰り返しますが、このテスト画像は、Joseph Paul Cohenによる Covid-19 Lung X-Ray setから得たもので、正常な胸部画像はAndrian YuがGradientCrescentリポジトリに収集しているオープンのKaggle Chest X-Ray setsに含まれるものです。 また、「テスト」セクションにリストされたとおり、独自に改善したトレーニングを使ってPyImageSearchのAdrianの構造を再利用しています。 また、テストデータベースを確認するために、X線画像とCT画像用のAIモジュール付きAWSクラウドベースOpen PACS Viewerを提供していただいたHYMにも感謝しています。

    次の内容

    今日、AIは、人間の健康と日常生活のほぼすべての側面を「浸食」しています。 単純化し過ぎた私の見解では、ヘルスケア分野におけるAI技術の応用には、以下のようないくつかの方向性があると考えられます。

    • 医用画像: 胸部、心臓、目、脳などのX線、CT、またはMRIなどの診断画像。
    • NLPによる読解: 膨大な量のテキスト資産とナレッジベースのマイニング、理解、学習。
    • 公衆衛生: 疫学などの傾向予測、分析、モデリングなど。 
    • パーソナル化AI: 個人専用の健康アシスタントとして、ともに成長して老化するように特別にトレーニングされたAI/ML/DLモデル。
    • その他のAI: AlphaGoや、Covid-19への対抗に活用できる3次元タンパク質構造予測向けのAlphaFoldなど。このような最先端の画期的な技術に非常に感激しています。 

    学習の過程で、焦点を当てられるものを検討していきたいと思います。 いずれにしても、在宅が長く続かない限りは、取り上げたいリストにしかすぎないかもしれません。 

    付録 - ファイルアップロードはこちらです。 上記で使用した画像と上記のJupyterノートブックファイルが含まれています。 週末に新規でセットアップして実行するには、数時間かかるかもしれません。

    0
    0 548
    記事 Toshihiko Minamoto · 11月 25, 2021 17m read

    キーワード:   Jupyterノートブック、TensorFlow GPU、Keras、ディープラーニング、MLP、HealthShare    

    1. 目的

    前回の「パート1」では、ディープラーニングデモ環境をセットアップしました。今回「パート2」では、それを使ってできることをテストします。

    私と同年代の人の中には、古典的なMLP(多層パーセプトロン)モデルから始めた人がたくさんいます。 直感的であるため、概念的に取り組みやすいからです。

    それでは、AI/NNコミュニティの誰もが使用してきた標準的なデモデータを使って、Kerasの「ディープラーニングMLP」を試してみましょう。 いわゆる「教師あり学習」の一種です。 これを実行するのがどんなに簡単かをKerasレベルで見ることにします。

    後で、その歴史と、なぜ「ディープラーニング」と呼ばれているのかについて触れることができます。流行語ともいえるこの分野は、実際に最近20年間で進化してきたものです。 

    HealthShareにも関連しているため、最終的には、少々実現的なユースケースを想像または予測できるようになることを願っています。

    2. 範囲と免責事項

    次のことを行います。 

    • tensorflow-gpu環境用に新しいJupyterカーネルをセットアップします。
    • ANNコミュニティで一般的な標準のMNISTサンプルを使って、Keras MLPモデルを定義、トレーニング、および検証(テスト)します。
    • 重要なパラメーターのほんの一部の非常に単純なものを簡単に説明します。
    • デモデータを簡単に調べます。データを理解することは、あらゆる実験において常に重要なことです。 
    • データサンプルをCache/HealthShareに保存し、予測(分類)と推論を行うために読み取り直す作業がどれほど簡単であるかを実演します。

    その後で、テストサンプルを少し回転させ、トレーニング済みのモデルをどれくらい混乱させられるかを確認し、それによって明確な制限を理解します。

    学術的・数学的な部分は省略しますが、仕組みについて簡単に説明するところもあります。

    免責事項: MNISTデータサンプルは、このデモの目的で公開されています。 ほとんどのデモコードは最小限に縮減されており、エラー処理の含まれないベアなコードでした。 Kerasコードのソースは「謝辞」に記載されています。  この内容は、必要に応じていつでも変更されます。

    3. 前提条件

    前の「パート1」の記事に記載されているとおりにデモ環境をセットアップする以外で、以下の実験のための前提条件はありません。 

    4. Jupyterノートブックのセットアップ

    前回インストールした「tensorflow-gpu」環境で次のコマンドを実行しました。 

    (tensorflow-gpu) C:\>conda install ipykernel
    Solving environment: done

    ... ...

    (tensorflow-gpu) C:\>python -m ipykernel install --user --name tensorflow-gpu --display-name "Tensorflow-GPU"
    Installed kernelspec tensorflow-gpu in C:\Users\zhongli\AppData\Roaming\jupyter\kernels\tensorflow-gpu

    こうすることで、「Tensorflow-GPU」などと呼ばれる新しいJupyterカーネルを作成しました。

    これで、Anaconda Promptから次のようにしてJupyterノートブックを起動できるようになりました。

    (tensorflow-gpu) C:\anaconda3\keras\Zhong>jupyter notebook
    [... ... 
    [I 10:58:12.728 NotebookApp] The Jupyter Notebook is running at:
    [I 10:58:12.729 NotebookApp] http://localhost:8889/?token=6b40f6e6749e88b80a338eec3330d06c181ead9b644cffe1
    [I 10:58:12.734 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
    [C 10:58:12.835 NotebookApp] ... ... 

    ブラウザのUIが以下のように起動していることを確認できます。 [New]をクリックすると、新しい「Tensorflow_GPU」タブが開きます。

    5. ディープラーニングMLPモデルのトレーニング

    標準のディープラーニングMLPモデルデモを試してみましょう。

     5.1 環境のテスト

    Jupyterタブを「MLP_Demo_ HS」のような名前に変更し、そのセル [1] で以下の行を実行して「Run」をクリックします。

    print('Hello World!')

    Hello World!

     

    5.2 PythonからHealthShareへの接続のテスト 

    セル[2]でPythonサンプルを実行し、まだ「パート1」の記事に記載されたとおりにHealthShareデータベースインスタンスに接続できるかをテストします。

    import codecs, sys
    import intersys.pythonbind3

    try:
        print ("Simple Python binding sample")

        port = input("Cache server port (default 56778)? ")
        port = port.rstrip()
        if (port == ""):
            port = "56778"

        url = "localhost["+port+"]:SAMPLES"
        print ("Connection string: " + url)

        print ("Connecting to Cache server")
        conn = intersys.pythonbind3.connection( )
        conn.connect_now(url, "_SYSTEM", "SYS", None)
        print ("Connected successfully")

        print ("Creating database")
        database = intersys.pythonbind3.database( conn)

        print ("Opening Sample.Person instance with ID 1 with default concurrency and timeout")
        person = database.openid( "Sample.Person", "1", -1, -1)

        print ("Getting the value of the Name property")
        name = person.get("Name")
        print ("Value: " + name)

        print ("Test completed successfully")
    except intersys.pythonbind3.cache_exception( err):
        print ("InterSystems Cache' exception")
        print (sys.exc_type)
        print (sys.exc_value)
        print (sys.exc_traceback)
        print (str(err))

    Simple Python binding sample
    Cache server port (default 56778)?
    Connection string: localhost[56778]:SAMPLES
    Connecting to Cache server
    Connected successfully
    Creating database
    Opening Sample.Person instance with ID 1 with default concurrency and timeout
    Getting the value of the Name property
    Value: Zevon,Mary M.
    Test completed successfully

     

    5.3 説明 - MLPモデルのトポロジーとMNISTデータセット

    MLPネットワークのトポロジーは、以下に示すとおり単純です。 通常、1つの入力と1つの出力のレイヤーがあり、多数の非表示レイヤーがあります。 

    各レイヤーには多数のニューロン(ノード)があります。 各ニューロンには活性化関数があります。 以下のように、2つの異なるレイヤーのニューロン間に完全にメッシュ化された接続(「密度」モデル)が存在することになります。 

    関連画像

    これに対応し、以下でテストしているKeras MLPモデルには次のものが含まれます。

    • 28 x 28ノードの計784ノードの入力レイヤー(ようするに28x28ピクセルの小さな画像であり、それぞれは「0」から「9」の手書きの数字です。MNISTデータセットにはこのような画像がトレーニング用に60,000個、テスト用に10,000個含まれています
    • 10ノードの出力レイヤー(0から9の間の入力画像の分類結果を表します)
    • 2つの非表示レイヤー(各レイヤーには512個のノードがあります)

    これが、このデモモデルの主要トポロジーです。 他の詳細については今のところは省略して、実際に実行することにしましょう。 

    関連画像  

    5.4. GoogleパブリッククラウドからMNISTサンプルデータを読み込む    

    では、上記のモデルをまったく最初から作成し始めることにしましょう。KerasパッケージとMNISTデータをJupyter Cellにロードして、  メニューの[Run]ボタンをクリックします。

    ### Import Keras modules 
    import keras
    from keras.datasets import mnist
    from keras.models import Sequential
    from keras.layers import Dense, Dropout
    from keras.optimizers import RMSprop

    ### Define key training parameter
    batch_size = 128  # weights adjusted in 128 steps
    num_classes = 10  # 10 classification results on the output layer
    epochs = 20       # run the set of samples 20 times.

    ###load the data from Google public cloud 
    # load the MNIST sample image  data, split between train and test sets
    (x_train, y_train), (x_test, y_test) = mnist.load_data()

    注意: 問題がある場合は、例外の内容に従うか(ほとんどの場合が、パッケージが見つからないといった例外です)、Googleで答えを探すか(99%の確率で回答を得られます)、以下に質問を投稿してください。

    コードの最後の行で、60,000個と10,000個の全データセットが3次元整数のPython配列にロードされました。  トレーニングサンプルの1つをHealthShareデータベースに読み込んでみてみましょう。

    5.5 データサンプルをHealthShareのグローバルに読み込む

    HealthShare -> SAMPLESネームスペース - > Sample.Person.clsで、この最も単純なクラスメソッドをスクラッチします。

    ClassMethod SetTrainGlobals(d1 As %Integer = , d2 As %Integer = , value As %String = "", target As %String = "") As %BigInt [ SqlProc ]
    {
    Set ^XTrainInput(d1, d2) = value
    Set ^YTrainTarget(d1) = target
    return $$$OK
    }

    1つの入力トレーニングサンプルを文字列としてグローバル^XTrainInputに取り込み、入力トレーニングターゲットを^YTrainTargetに保存します。

    リコンパイルし、セクション5.2に従って接続を更新してから、以下のようにPythonのセルから呼び出しを実行します。

    result1 = person.run_obj_method("SetTrainGlobals", [0, 2, str(x_train[0]), str(y_train[0])])

    HealthShare -> Samplesで、^XTrainGlobal(0, 2) というグローバルが2次元整数の文字列で作成されたことがわかります。

    後で、別の単純なメソッドを実行し、データをサンプルとしてPython変数に読み戻すことができます。  

    5.6 モデルを実行してトレーニング実行する

    JupyterでのMLPモデルの定義とトレーニングを完了しましょう。

    基本的に以下のコードの「reshape」は、それぞれの28 x 28のサンプルを0から255の値の1x 784個の値に変換し、その後で0から1.0の浮動小数点型に正規化します。

    Model.SequentialからModel.Summaryまでのコードは、784 x 512 x 512 x 10ノードのMLPを、「relu」活性化関数を使って定義するするものです。 

    最後に、 model.fitでトレーニングし、model.evaluteでテスト結果を評価します。   

    x_train = x_train.reshape(60000, 784)
    x_test = x_test.reshape(10000, 784)
    x_train = x_train.astype('float32')
    x_test = x_test.astype('float32')
    x_train /= 255
    x_test /= 255

    print(x_train.shape[0], 'train samples')
    print(x_test.shape[0], 'test samples')

    # convert class vectors to binary class matrices
    y_train = keras.utils.to_categorical(y_train, num_classes)
    y_test = keras.utils.to_categorical(y_test, num_classes)

    model = Sequential()
    model.add(Dense(512, activation='relu', input_shape=(784,)))
    model.add(Dropout(0.2))
    model.add(Dense(512, activation='relu'))
    model.add(Dropout(0.2))
    model.add(Dense(num_classes, activation='softmax'))

    model.summary()

    model.compile(loss='categorical_crossentropy',
                  optimizer=RMSprop(),
                  metrics=['accuracy'])

    history = model.fit(x_train, y_train,
                        batch_size=batch_size,
                        epochs=epochs,
                        verbose=1,
                        validation_data=(x_test, y_test))

    score = model.evaluate(x_test, y_test, verbose=1)

    print('Test loss:', score[0])
    print('Test accuracy:', score[1])

    Using TensorFlow backend.
    60000 train samples
    10000 test samples
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #
    =================================================================
    dense_1 (Dense)              (None, 512)               401920
    _________________________________________________________________
    dropout_1 (Dropout)          (None, 512)               0
    _________________________________________________________________
    dense_2 (Dense)              (None, 512)               262656
    _________________________________________________________________
    dropout_2 (Dropout)          (None, 512)               0
    _________________________________________________________________
    dense_3 (Dense)              (None, 10)                5130
    =================================================================
    Total params: 669,706
    Trainable params: 669,706
    Non-trainable params: 0
    _________________________________________________________________
    Train on 60000 samples, validate on 10000 samples
    Epoch 1/20
    60000/60000 [==============================] - 11s 178us/step - loss: 0.2476 - acc: 0.9243 - val_loss: 0.1057 - val_acc: 0.9672
    Epoch 2/20
    60000/60000 [==============================] - 6s 101us/step - loss: 0.1023 - acc: 0.9685 - val_loss: 0.0900 - val_acc: 0.9730
    Epoch 3/20
    60000/60000 [==============================] - 6s 101us/step - loss: 0.0751 - acc: 0.9780 - val_loss: 0.0756 - val_acc: 0.9783
    Epoch 4/20
    60000/60000 [==============================] - 6s 100us/step - loss: 0.0607 - acc: 0.9816 - val_loss: 0.0771 - val_acc: 0.9801
    Epoch 5/20
    60000/60000 [==============================] - 6s 101us/step - loss: 0.0512 - acc: 0.9844 - val_loss: 0.0761 - val_acc: 0.9810
    Epoch 6/20
    60000/60000 [==============================] - 6s 102us/step - loss: 0.0449 - acc: 0.9866 - val_loss: 0.0747 - val_acc: 0.9809
    Epoch 7/20
    60000/60000 [==============================] - 6s 101us/step - loss: 0.0377 - acc: 0.9885 - val_loss: 0.0765 - val_acc: 0.9811
    Epoch 8/20
    60000/60000 [==============================] - 6s 101us/step - loss: 0.0334 - acc: 0.9898 - val_loss: 0.0774 - val_acc: 0.9840
    Epoch 9/20
    60000/60000 [==============================] - 6s 101us/step - loss: 0.0307 - acc: 0.9911 - val_loss: 0.0771 - val_acc: 0.9842
    Epoch 10/20
    60000/60000 [==============================] - 6s 105us/step - loss: 0.0298 - acc: 0.9911 - val_loss: 0.1015 - val_acc: 0.9813
    Epoch 11/20
    60000/60000 [==============================] - 6s 102us/step - loss: 0.0273 - acc: 0.9922 - val_loss: 0.0869 - val_acc: 0.9833
    Epoch 12/20
    60000/60000 [==============================] - 6s 99us/step - loss: 0.0247 - acc: 0.9926 - val_loss: 0.0945 - val_acc: 0.9824
    Epoch 13/20
    60000/60000 [==============================] - 6s 101us/step - loss: 0.0224 - acc: 0.9935 - val_loss: 0.1040 - val_acc: 0.9823
    Epoch 14/20
    60000/60000 [==============================] - 6s 100us/step - loss: 0.0219 - acc: 0.9939 - val_loss: 0.1038 - val_acc: 0.9835
    Epoch 15/20
    60000/60000 [==============================] - 6s 104us/step - loss: 0.0227 - acc: 0.9936 - val_loss: 0.0909 - val_acc: 0.9849
    Epoch 16/20
    60000/60000 [==============================] - 6s 100us/step - loss: 0.0198 - acc: 0.9944 - val_loss: 0.0998 - val_acc: 0.9826
    Epoch 17/20
    60000/60000 [==============================] - 6s 101us/step - loss: 0.0182 - acc: 0.9951 - val_loss: 0.0984 - val_acc: 0.9832
    Epoch 18/20
    60000/60000 [==============================] - 6s 102us/step - loss: 0.0178 - acc: 0.9955 - val_loss: 0.1150 - val_acc: 0.9839
    Epoch 19/20
    60000/60000 [==============================] - 6s 100us/step - loss: 0.0167 - acc: 0.9954 - val_loss: 0.0975 - val_acc: 0.9847
    Epoch 20/20
    60000/60000 [==============================] - 6s 102us/step - loss: 0.0169 - acc: 0.9956 - val_loss: 0.1132 - val_acc: 0.9832
    10000/10000 [==============================] - 1s 71us/step
    Test loss: 0.11318948425535869
    Test accuracy: 0.9832 

     

    以上で、「トレーニング済み」となりました。 ほんの数行のコードで、このKerasディープラーニングMLPは、「tensorflow-gpu」環境でかなり効率的に実行します。 これまでのすべてのキットインストールを検証します。 

    6 サンプルを使用してモデルをテストする

    以下の指定されたサンプルを使用してトレーニング済みのモデルをテストしましょう。

    10,000個のx_testセットから特定のサンプルをランダムに選択し、別のHealthShareグローバルに保存してから、そのグローバルからPython配列にデモサンプルとして読み戻します。 それを使用してトレーニング済みのモデルをテストします。

    次に、この入力サンプルを90度、180度、および270度に回転させてモデルを再テストし、混乱が生じないかを確認します。 

    6.1 サンプルをHealthShareに保存する - デモ 

    サンプルをランダムに選択しましょう。たとえば、10,000個のテストサンプルから12番目のサンプルを選択し、HSグローバルに保存します。

    HealthShare -> SAMPLE -> Sample.Personクラスに新しいクラスメソッドを追加します。

    ClassMethod SetT2Globals(d1 As %Integer = 0, d2 As %Integer = 0, d3 As %Integer = 0, value As %String = "", target As %String = "") As %BigInt [ SqlProc ]
    {
             Set ^XTestInput(d1, d2, d3) = value
             Set ^YTestTarget(d1, d2) = target
             return $$$OK
    }

    Sample.Person.cls をリコンパイルします。  Jupyterノートブックで、セクション5.2のコードを再実行し、データベースのバインディングを更新します。次に、この行を実行して、28 x 28 個の数字サンプルをグローバル^XTestInputに保存します。

    import re
    n = 12  # randomly choose a sample 
    for i in range(0, len(x_train[n])):
         r1 = person.run_obj_method("SetT2Globals", [1, n, i, re.sub('0\s0', '  0   0', str(x_test[n][i])), str(y_test[n])])

    これで、2次元配列のサンプルがHS SAMPLEグローバル^XTestInputに保存されていることがわかりました。 それぞれの数字は、0~255のピクセルグレースケールです。 以下のHS管理ポータルから、それが「9」であることが簡単にわかります。

       

    6.2 HealthShareグローバルからサンプルを読み取る - デモ

    HSデータベースグローバルからサンプルを確実に読み取ることができます。  

    別のクラスメソッドをSample.Person.clsに追加して、それをリコンパイルします。

    ClassMethod GetT2Globals(d1 As %Integer = 0, d2 As %Integer = 0, d3 As %Integer = 0) As %String [ SqlProc ]
    {
         Set value = ^XTestInput(d1, d2, d3)
         return value
    }

    Jupyterで、前述のようにDBバインディングを更新してから、このPythonコードを実行してHealthShareから文字列としてグローバルを読み取り、1 x 2次元数値配列に変換します。

    import re, ast
    sample = ""
    for i in range(0, len(x_train[n])):
       sample += person.run_obj_method("GetT2Globals", [1, n, i])
    #convert it to numpy ndarray
    as1  = np.array(ast.literal_eval(re.sub('\s+', ',', re.sub('0\]', '0', re.sub('\[  ', '', re.sub('\]\[', ' ', sample))))))   
    Sample12 = as1.reshape(1, 28, 28)
    print(Sample12)

    6.3 トレーニング済みモデルをテストする 

    これで、Jupyterで、この配列「Sample12」をトレーニング済みのモデルに送信できるようになりました。  model.predictmodel.predict_classesがこの作業を行います。

    Sample12  = Sample12.reshape(1, 784)
    Sample12f = Sample12.astype('float32')/255  # normalise it to float between [0, 1]
    Result12f = model.predict(Sample12f)   #test the 1x784 sample, the result is a 1d matrix
    print(Result12f)

    Result12 = model.predict_classes(Sample12f)  #test the sample, the result is a clasified lable.
    print(Result12)

    [[2.5672970e-27 1.1168821e-25 1.3736557e-20 6.2964843e-17 7.0107062e-09 6.2905544e-17 1.5294099e-28 7.8019199e-17 3.5748028e-16 1.0000000e+00]]
    [9]

    結果には出力レイヤーのニューロン#9に最大値「1.0」があることが示されているため、分類結果は「9」となります。  これは正しい結果です。

    確かに、人工的な28 x 28整数のサンプルをモデルに送信して試してみることができます。

    6.4 サンプルを回転させてモデルを再テストする

    このサンプルを反時計回りに90度回転させて、もう一度試してみることはできるでしょうか? 

    Sample12 = Sample12.reshape(28, 28)  #reshape to 2D array values
    Sample1290 = np.rot90(Sample12)  #rotate in 90 degree
    print(Sample1290)

    次に、モデルを再テストします。

    Sample12901  = Sample1290.reshape(1, 784)
    Sample1290f = Sample12901.astype('float32')/255  
    Result1290f = model.predict(Sample1290f)   
    print(Result1290f)

    Result1290 = model.predict_classes(Sample1290f)  
    print(Result1290)

    [[2.9022769e-05 1.2192334e-20 1.7143857e-07 3.0004558e-11 2.4583075e-11 6.2443775e-01 2.5749558e-05 3.7550735e-01 2.0722151e-08 5.5368415e-10]]
    [5]

    さて、モデルは「5」と認識しました。ニューロン#5が最大出力値でトリガーされています。  どうやら少し混乱しているようです! (確かに縦向きのものが「5」のように見えるので仕方ありませんね)。

    では今度はサンプルを180度回転させましょう。モデルはどのように認識するでしょうか?  

    [[3.3131425e-11 3.0135434e-27 8.7524540e-23 7.1371946e-24 2.4029167e-13 4.2327470e-09 1.0000000e+00 1.7086377e-18 1.3129146e-18 2.8595145e-22]]
    [6]

    もちろん、間違いなく「6」と認識しました!  人間も「9」ではなく「6」と認識するでしょう。 

    では、最後に270度回転させてみましょう。 

    どうやら、また混乱しているようです。今度は「4」と認識しました。

    [[1.6130849e-06 3.0311636e-14 2.1490927e-03 2.7108688e-03 9.9499077e-01 1.4130991e-04 6.2298268e-06 8.6649310e-09 2.9320630e-12 1.5710594e-07]]
    [4]

     

    6.5 パブリッククラウドツールとの比較

    上記の配列をPythonコードの行を介してPNGにエクスポートし、回転して、反転して、画像上にまとめました。 次のようになっています。

     

    次に、それぞれ「Google Vision API」、「Amazon Rekognition」、および「Microsoft Computer Vision API」にアップロードすると、結果はどうなるでしょうか。

    この場合、AWSの「数字」のスコアが95%とわずかに最高となっています(これは絶対に代表的な結果ではありません)。 

    1. Google Vision APIの結果:

     

    2. AWS Rekognitionの結果:

    3. Microsoft Computer Visionの結果: 

     

    7. 次の内容

    次は、以下のような他のいくつかの簡単なポイントについてフォローアップします。 

    • 一言で、MLPはどのように機能するのか?
    • 制限と考えられるユースケースは?
    • 現在のスタックで実行可能な最も一般的なML/DL/ANNモデルの簡単なウォークスルー  
    • 謝辞 
    0
    0 181
    記事 Toshihiko Minamoto · 11月 18, 2021 13m read

    Python 3をHealthShareにバインディングした深層学習デモキット(パート1) キーワード:  Anaconda、Jupyterノートブック、TensorFlow GPU、ディープラーニング、Python 3、HealthShare    

    1. 目的

    この「パート1」では、Python 3をHealthShare 2017.2.1インスタンスにバインドして、「単純」かつ一般的なディープラーニングデモ環境をセットアップする方法を段階的に簡単に説明します。  私は手元にあるWin10ノートパソコンを使用しましたが、このアプローチはMacOSとLinuxでも同じように実装できます。

    先週、PYPL Indexにおいて、Pythonが最も人気のある言語としてJavaを超えたことが示されました。  TensorFlowも研究や学術の分野において非常に人気のある強力な計算エンジンです。 HealthShareは、ケア提供者に患者の統一介護記録を提供するデータプラットフォームです。

    これらを1つのキットにまとめることはできるでしょうか。また、これを実現する上での最も単純なアプローチは何でしょうか。  ここでは、このトピックの最初のステップを一緒に試してから、次に試すことのできるデモについて検討しましょう。  

    2. 範囲と免責事項

    ここで構築しようとしているデモキットには、次のコンポーネントが含まれます。

    •  Anaconda - 十分に構造化されたパッケージ管理で人気のあるデータサイエンスプラットフォーム。 
    • TensorFlow - 強力な計算エンジンを備えた機械学習プラットフォーム。
    • Theano - ML用計算エンジンのPythonライブラリ。 オプションで、TensorFlowの代替として利用できます。
    • Keras - TensorFlowまたはTheanoエンジン上に配備されるPython Deep Learningライブラリ。 
    • Jupyterノートブック - Anacondaパッケージに含まれます。
    • Python 3 - Anacondaパッケージに含まれます。
    • HealthShare - HealthShare 2017.2.1インスタンスを使用します。

    ... ... そして、Win10ノートパソコンです。   

    私のDellノートパソコンにはたまたまローエンドのGPU GeForce 840Mが搭載されています。強力ではありませんが、それでもデモのトレーニング速度は3倍高められます。

    それ以外は、以下のすべてのステップはMacOS(私のはMac Miniです)かLinux環境(たとえばAWSでUbuntuも試しました)で十分に機能します。  

    HealthShare 2017インスタンスは、便宜上、デモDBとして使用されていますが、ここで始めようとしている基本的な実験は、CacheまたはEnsembleのインスタンスにおいても機能し、最終的にはIRISにおいても機能すると思っています。  

    免責事項: これは、PoCデモキットを一緒に使用することで、コミュニティの関心を引くことを狙いとした記事です。 何らかの手段または合法的なブランドによる公式リリースまたは正式発表では決してありません。 内容や範囲は、いずれ空きのある時間に、必要に応じて変更されることがあります。 

    3. 前提条件

    ここでは、ANN、AI、ディープラーニング、またはTensorFlowなどの知識や使用経験は必要ありません。 Python初心者でも十分に使用できます。

    ノートパソコンやサーバーにGPUは必要ありません。 ただし、搭載されているのであれば、そのドライブを更新する必要があるかもしれません。 私の場合はここからドライバーをダウンロードしました。

    ちょっとした注意: 最後の警告7.1を参照してください。

    4. デモ環境のセットアップ - 手順

    4.1 Anaconda環境のインストール

    4.1.1 ダウンロードとインストール 

    次のリンクからAnacondaリリースをダウンロードします。この記事を書いた2019年3月時点でのダウンロード最新バージョンは、Anaconda 2018.12(Python 3.7)です。

    このリンクにある簡単な画面ごとの段階的な指示に従って、Anacondaベース環境をインストールします。 

    _注意: 同じリンクから、MacOSまたはLinuxホスト用のリリースをダウンロードできます。 _

    4.1.2 Anacondaインストールのテスト

    このリンクに従ってインストールを段階的にテストし、PythonとJupyterノートブックが正しく動作することを確認します。ただし、Spyder IDEは必要ありません。 

    リンク先の指示にあるように、WindowsスタートまたはWindowsコマンドラインからJupyterノートブックを起動できることを確認します。

    4.2 TensorFlow環境のインストール 

    Anaconda PromptウィンドウでCondaコマンドを使用して、TensorFlowエンジンをAnacondaパッケージとしてインストールします。

    手順は次のとおりです。

                1. Anaconda Promptウィンドウを起動します。CondaバージョンとPythonバージョンを確認してください。

    (base) C:\>conda -V

    conda 4.5.12

    (base) C:\>python -V
    Python 3.7.1

               2. 「tensorflow」という新しい環境を作成して、アクティブ化します。

    (base) C:\>conda create -n tensorflow
    Solving environment: done

    .... ... ... ...
    (base) C:\>conda activate tensorflow

    (tensorflow) C:\>

               3. 指定されたCondaチャンネルを介して、TensorFlowパッケージをインストールします。

    (tensorflow) C:\>conda install -c conda-forge tensorflow

    このステップでは、非常に大量の具体的なパッケージをダウンロード、解決、アップグレード/ダウングレードするため、かなりの時間が掛かります。

              4. TensorFlowが正常にインストールされたことをテストします。

     tensorflow) C:\>python
    Python 3.6.7 (default, Feb 28 2019, 07:28:18) ... .... ... ... 
    >>> import tensorflow as tf
    >>> se=tf.Session()
    >>> print(se.run(tf.constant('Hello Tensorflow')))
    b'hello TensorFlow!'

    >>> print(tf.__version__)
    1.10.0

    >>> quit()

    (tensorflow) C:\>

     

    4.3 GPU環境のインストール

    TensorFlow GPU環境と同様の手順でインストールします。

    1. 「Tensorflow」という新しい環境を作成して、アクティブ化します。

    (base) C:\>conda create -n tensorflow-gpu
    Solving environment: done

    ... ... 
    (base) C:\>conda activate tensorflow-gpu

    (tensorflow-gpu) C:\>

    2. チャンネルを指定せずに(「tensorflow」ではなく)「tensorflow-gpu」パッケージをインストールします。 

    (tensorflow-gpu) C:\>conda install tensorflow-gpu 

    この手順では、すべてのパッケージのダウンロード、解決、インストールに時間が掛かります。

    3. インストールを検証します。 

    >>> import tensorflow as tf
    >>> se = tf.Session()
    2019-03-14 14:02:52.423895: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX AVX2
    2019-03-14 14:02:53.081332: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1433] Found device 0 with properties:
    name: GeForce 840M major: 5 minor: 0 memoryClockRate(GHz): 1.124
    pciBusID: 0000:03:00.0
    totalMemory: 2.00GiB freeMemory: 1.65GiB
    2019-03-14 14:02:53.087514: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1512] Adding visible gpu devices: 0
    2019-03-14 14:08:18.789761: I tensorflow/core/common_runtime/gpu/gpu_device.cc:984] Device interconnect StreamExecutor with strength 1 edge matrix:
    2019-03-14 14:08:18.794278: I tensorflow/core/common_runtime/gpu/gpu_device.cc:990]      0
    2019-03-14 14:08:18.796348: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1003] 0:   N
    2019-03-14 14:08:18.804907: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1115] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 1395 MB memory) -> physical GPU (device: 0, name: GeForce 840M, pci bus id: 0000:03:00.0, compute capability: 5.0)

    注意: この手順ではCUDA gpuライブラリが再構築されているため、かなりの時間が掛かることがあります。 問題がある場合は、Anaconda Promptを「管理者として実行」で再起動し、この手順を再試行してください。 私の問題は、2回ほど再起動して解決されました。 

    >>> print(se.run(tf.constant('Hello')))
    b'Hello'
    >>> print(tf.__version__)
    1.13.1

    では、このAnacondaの環境をリスト表示しましょう。 

    (base) c:\>conda env list
    # conda environments:
    #
    base                  *  C:\ProgramData\Anaconda3
    tensorflow               C:\Users\zhongli\AppData\Local\conda\conda\envs\tensorflow
    tensorflow-gpu           C:\Users\zhongli\AppData\Local\conda\conda\envs\tensorflow-gpu

     

    4.4 Theano環境のインストール(オプション)

    Keras構成を試す予定がなく、常にTensorFlowを基盤のエンジンとして使用するのであれば、この手順を省略できます。

    そうでない場合は、新しい環境を作成してアクティブ化し、次のコマンドを実行してください: conda install -c conda-forge theano

    4.5 Kerasのインストール

    Kerasは、このディープラーニングデモスタックの最上位のレイヤーモジュールです。 

    今作成した「tensorflow-gpu」環境にインストールしましょう。  注意: ここでは「keras」ではなく「keras-gpu」をインストールしています。

    (tensorflow-gpu) c:\>conda install -c anaconda keras-gpu

    関連するパッケージがインストールされます。 では、簡単な検証を行いましょう。

    (tensorflow-gpu) c:\>python
    Python 3.6.8 |Anaconda, Inc.| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import keras as k
    Using TensorFlow backend.

    「パート2」では、ディープラーニングモデルの実行にJupyterノートブックを使用して、上記でインストールしたすべてのコンポーネントを検証しますが、 その前に、HealthShareインスタンスのインストールとPython 3バインディングを完了しましょう。

    4.6. HealthShareインスタンスのインストール

    私はIntersystems WRCのダウンロードページから標準のHealthShareインストールファイルをダウンロードしました。   

    hscore15.032_hsaa15.032_hspi15.032_hsviewer15.032_linkage15.032  Windows 64 Bit EXE

    また、いつものように一時ライセンスが必要となりますが、これはInterSystemsの営業部またはサポートに連絡すると取得できます。

    インストールが完了すると、バージョン文字列は次のようになります: Cache for Windows (x86-64) 2017.2.1 (Build 801_3_18095U) Mon May 7 2018 14:28:58 EDT [HealthShare Modules:Core:16.0.7669 + Linkage Engine:16.0.7669 + Patient Index:16.0.7669 + Clinical Viewer:16.0.7669 + Active Analytics:16.0.7669]

    4.7 HealthShareへのPythonバインディングをTensorFlow環境にインストール  

    4.7.1 HealthShareインスタンスパスの特定

    製品の公式ドキュメント『Python Binding to Intersystems Cache』はこちらにあります。

    たとえば、私のHSインスタンスは次のローカルの場所にインストールされています: C:\InterSystems\HS20181

    そのため、まず、このISC Python DLLパスをWindowsの「環境パス」に追加しました: C:\InterSystems\HS20181\bin

    4.7.2 VC++コンパイラのセットアップ 

    次に、「vcvarsall.bat」コマンドを実行して、パスがMS VC++コンパイラ用にセットアップされていることを確認しました。 

    私の場合は、**[Visual Studio 2017]->[Tools]->[Get Tools and Features]**メニューに移動し、以下のように「Desktop Development with C++」がインストールされている(選択済み)ことを再確認しています。この選択済みのチェックがない場合は、インストールする必要があります。  すると、デフォルトのディレクトリ(例: **C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build **)に「vcvarsall.bat」が表示されるようになります。

    4.7.3 HealthShareインスタンスへのPython 3バインディングのインストール

    次に、前のセクションでインストールした「tensorflow-gpu」環境に直接「HealthShareへのPython 3バインディング」をセットアップします。

    注意: 私は「...setup.py ...」の代わりに「python setup3.py install」を実行しました。

    (tensorflow-gpu) c:\>cd c:\InterSystems\HS20181\dev\python

    (tensorflow-gpu) c:\InterSystems\HS20181\dev\python>python setup3.py install
    enter directory where you installed Cache'c:\InterSystems\HS20181
    libdir=c:\InterSystems\HS20181/dev/cpp/lib
    include dir=c:\InterSystems\HS20181/dev/cpp/include
    libs=['cbind']
    running install
    running bdist_egg
    running egg_info
    ... ...
    installing library code to build\bdist.win-amd64\egg
    running install_lib
    running build_py
    running build_ext
    creating build\bdist.win-amd64\egg
    creating build\bdist.win-amd64\egg\intersys
    copying build\lib.win-amd64-3.6\intersys\pythonbind.py -> build\bdist.win-amd64\egg\intersys
    ... ...

    creating stub loader for intersys\pythonbind31.cp36-win_amd64.pyd
    byte-compiling build\bdist.win-amd64\egg\intersys\pythonbind31.py to pythonbind31.cpython-36.pyc
    creating build\bdist.win-amd64\egg\EGG-INFO
    ... ...

    Processing pythonbind3-1.0-py3.6-win-amd64.egg
    creating c:\users\zhongli\appdata\local\conda\conda\envs\tensorflow-gpu\lib\site-packages\pythonbind3-1.0-py3.6-win-amd64.egg
    Extracting pythonbind3-1.0-py3.6-win-amd64.egg to c:\users\zhongli\appdata\local\conda\conda\envs\tensorflow-gpu\lib\site-packages
    Adding pythonbind3 1.0 to easy-install.pth file

    Installed c:\users\zhongli\appdata\local\conda\conda\envs\tensorflow-gpu\lib\site-packages\pythonbind3-1.0-py3.6-win-amd64.egg
    Processing dependencies for pythonbind3==1.0
    Finished processing dependencies for pythonbind3==1.0

    4.7.4 バインディングがHealthShareデータベースインスタンスで機能していることを確認

    では、「samples3」パス内の「test.py」を実行しましょう。

    (tensorflow-gpu) c:\InterSystems\HS20181\dev\python\samples3>python test.py
    Simple Python binding sample
    Cache server port (default 1972)? 56778
    Connection string: localhost[56778]:SAMPLES
    Connecting to Cache server
    Connected successfully
    Creating database
    Opening Sample.Person instance with ID 1 with default concurrency and timeout
    Getting the value of the Name property
    Value: Zevon,Mary M.
    Test completed successfully

    上記により、特定のHealthShareデータベース(SAMPLESネームスペース: Sample.Person テーブル)へのPython 3バインディングがtensorflow-gpu環境内で機能していることが確認されました。 

    5. 次の内容

    これで、実際のディープラーニングデモを試すための一般的なツールを使用した基本環境を用意できました。

    次の「パート2」では、Jupyterノートブックを使用してサンプルコードを実行し、ほとんどの人が最初に使用する最も古典的なモデルを手始めに、さまざまなモデルや機能の重要な概念を探っていきましょう。 最終的に実際のケースを想像したり収集したりする前に、一般的なサンプルデータをトレーニングまたは予測する上で、最も単純なアプローチや近道を探ります。  

    6. 謝辞  

    ここに記載したほとんどの作業は、静かなクリスマスと年末年始の休暇中にやり直されたものです。 ここに記載されているデモキットについては、HealthShareインスタンスへのPythonバインディングを実行することだけが追加作業として必要でした。

    7. 警告

    7.1 GPU ドライバーの更新: GPUドライバーを直接更新すると、Matlabや一般的なゲーミングプラットフォームなどのほかのアプリケーションに問題が発生することがあると言われました。そこで、修正をいじることに慣れている必要があります。

    7.2 簡単なショートカット: 可能な限り、単純なコードやショートカットを使用しようとしています。  考えられる実際のケースや問題については、モデルパラメーターの最適化やデータ正規化に関する正式な方法は常に大変なタスクであるため、ここでのデモデータセットにおいて考慮する必要はありません。 

    0
    0 284
    記事 Toshihiko Minamoto · 8月 4, 2021 4m read

    皆さんこんにちは! よろしければ、ボットが対話できるようになるようお手伝いいただけませんか?

    チャットボットはこちらからアクセスしてください: Help my chatbots to talk!

    なんだ、そのチャットボットはスマートではないのですか?

    このシナリオでは「スマート」は適切な言葉ではありません。 チャットボットはトレーニング済みではありますが、少量のデータでしかトレーニングされていません! ほとんどのチャットボットソリューションでは、機械学習を使用して人間と対話する方法が作成されており、機械学習がうまく機能するには、重要なものが 1 つ必要となります。それはデータです。 

    どのように動作しますか?

    簡単に説明しましょう。脳があっても人生経験のない人がいるとします。生まれたばかりの赤ん坊です。 この状況では、その人(赤ん坊)は、話している人を見たり、授業を受けたり、映画を見たりなどして、話し方を学習しなければなりません。 この人間の学習プロセスを、機械学習モデルにも当てはめることができます。 機械学習モデルが学習できる状況を与える必要があり、その状況がデータということになります。

    ** つまり、チャットボットは単なる辞書かオウムということになるのでしょうか…**

    全くもってそうではありません。 このアプローチを採用する上での最初の問題は、チャットボットが受け取る各文に対してどのように答えるのかを誰かが教えなければならない、ということです。 この問題に取り組んで、異なる文をいくつ形成できるのかを予測することもできますが、明らかに、この問題を解決する方法ではありません。 この問題を解くための機械学習テクニックはたくさん存在します。特定のデータ量でモデルをトレーニングした後にトレーニングデータの次のトピックでうまく動作するようにするのです。 このために、プログラムはほかの会話のデータを使用して、パターン、単語、同義語、意味を学習します。そしてこのプロセスが機械学習モデルとなるのです。

    このチャットボットは一体何を実行できますか?

    この時点では、文の意図を予測し、その予測をもとに回答することしかできません。 ボットをトレーニングした後は、その「意識」に以下のような意図しかないと考えてください。

    • 意図: 「こんにちは」、返答: 「やあ!」
    • 意図: 「さようなら」、返答: 「またね!」
    • 意図: 「話しかけないで。」 、返答: 「わかった。」

    そして、人間がボットに「ここまで車できましたか?」という文を送信します。 人間にとっては、この文には意図と何ら関係がないことに注意してください。 しかし、おそらくモデルは「意図に関連する可能性」として低い精度でスコア付けを行い、次のように返答するでしょう。

    「ここまで車で来ましたか?」という文に対して、私が見つけた最も意義のある意図です。

    • さようなら: 5% の精度
    • こんにちは: 3% の精度

    つまり、この相関関係によれば、おそらくチャットボットは「さようなら!」と返答するように決定することでしょう。 しかし、なぜ返答するのでしょうか。 どのように返答すればよいのかがわからないのであれば、黙っている方がよいのではないでしょうか。 おそらく、浅はかな説明をすれば、ボットは考えられる最善の答えで返答すれば、少なくともあなたに関心がないと思われないのではないかと考えたのでしょう。

    ボットをトレーニングすれば、作業はそれで終わりですか?

    ほとんどの場合、それで終わりではありません。 ボットがうまく動作し始めたとしても、時間が経つごとに精度が失われてしまう確率が十分にあります。 高齢者と若者の話し方の違いを考えてみればわかるように、言語は生ものであり、話し方は常に変化しています。チャットボットについて言えば、新しいデータでトレーニングしたり、新しいテクニックで予測したりする必要があるのです。 つまり、モデルのパフォーマンスを定期的に監視し、新しいデータを使ってモデルを再トレーニングするのが一般的です。 私が今取り組んでいるのはまさにこれです! Help my chatbots to talk!

    この記事が役に立った場合、またはコンテンツを気に入った場合は、投票してください。
    このアプリケーションは、Open Exchange コンテストに現在参加中です。こちらから私のアプリケーション「iris-python-suite」に投票してください。

    0
    0 167
    記事 Toshihiko Minamoto · 8月 3, 2021 6m read

    画像

    IRIS と Python でチャットボットを作成する

    この記事では、InterSystems IRIS データベースを Python と統合して自然言語処理(NLP)の機械学習モデルを提供する方法を説明します。

    Python を使用する理由

    世界的に広く採用され使用されている Python には素晴らしいコミュニティがあり、様々なアプリケーションをデプロイするためのアクセラレータ/ライブラリが豊富に提供されています。 関心のある方は https://www.python.org/about/apps/ をご覧ください。

    IRIS のグローバル

    ^globals について学び始めると、型にはまらないデータモデルに素早くデータを取り込む手法として使用することに慣れてきました。 そのため、最初は ^globals を使用してトレーニングデータと会話を保存し、チャットボットの動作をログに記録することにします。

    自然言語処理

    自然言語処理(NLP)は、人間の言語から意味を読み取って理解する能力を機械に与える AI のテーマです。 ご想像のとおりあまり単純ではありませんが、この広大で魅力的な分野で最初の一歩を踏み出す方法を説明します。

    デモ - 試してみましょう

    チャットボットアプリケーションをデモとしてデプロイしています: http://iris-python-suite.eastus.cloudapp.azure.com:8080

    どのように動作しますか?

    機械学習

    まず、機械学習には、一般的なソフトウェア開発とは異なるパラダイムがあることを知っておきましょう。 分かりにくい要点は、機械学習モデルの開発サイクルです。

    完全な説明ではありません

    標準的なアプリケーション開発サイクルは次のようになっています。

    コードの開発 -> テスト(開発データを使用)-> デプロイ(実際のデータを使用)

    そして、機械学習では、コード自体に同じ価値がありません。 責任はデータと共有されています! つまり、データがなければ、実際のデータもないということです。 これは、実行される最終的なコードは、開発コンセプトと使用されるデータのマージによって生成されるためです。 したがって、機械学習アプリケーションのサイクルは次のようになっています。

    モデルの開発(トレーニング)と実データ -> 検証 -> この結果(モデル)をデプロイ

    モデルをトレーニングするには

    モデルをトレーニングするにはたくさんの手法があり、それぞれのケースと目的には大きな学習曲線が伴います。 ここでは、いくつかの手法をカプセル化して、トレーニング方法と事前処理済みのトレーニングデータを提供する ChatterBot ライブラリを使用し、結果に焦点を当てやすくしています。

    事前学習のモデル言語とカスタムモデル

    基本的な会話型チャットボットを作成するにはこれを使用することができます。 チャットボットをトレーニングするためのすべてのデータがニーズを完全に満たすように作成することもできますが、短時間で作成するのは困難です。 このプロジェクトでは、会話のベースとして en_core_web_sm を使用し、フォームを使って作成できるカスタムトレーニングデータとマージします。

    基本アーキテクチャ

    画像

    Python で使用したもの

    このアプリケーション環境では、Python 3.7 と以下のモジュールを使用します。

    • PyYAML<=5.0.0
    • dash==1.12.0
    • dash-bootstrap-components==0.10.1
    • dash-core-components==1.10.0
    • dash-html-components==1.0.3
    • dash-renderer==1.4.1
    • dash-table==4.7.0
    • plotly==4.7.1
    • numpy==1.18.4
    • networkx==2.4
    • Flask>=1.0.0
    • chatterbot>=1.0.0
    • chatterbot-corpus>=1.2.0
    • SQLAlchemy>=1.2
    • ./nativeAPI_wheel/irisnative-1.0.0-cp34-abi3-linux_x86_64.whl

    プロジェクト構造

    このプロジェクトは理解しやすい単純な構造になっています。 メインのフォルダには、最も重要な 3 つのサブフォルダがあります。

    • ./app: すべてのアプリケーションコードとインストール構成が含まれます。
    • ./iris: アプリケーションをサービングする InterSystems IRIS dockerfile が含まれます。
    • ./data: ボリュームによってホストをコンテナー環境にリンクします。

    アプリケーション構造

    ./app ディレクトリには、以下のファイルが含まれます。

    • chatbot.py: Web アプリケーションの実装が含まれます。
    • iris_python_suite.py: IRIS Native API によって IRIS データベースや Python と使用する、アクセラレータを持つクラスです。

    データベース構造

    このアプリケーションでは、InterSystems IRIS をリポジトリとして使用します。使用されるグローバルは以下のとおりです。

    • ^chatbot.training.data: 質疑応答の形式で、すべてのカスタムトレーニングデータを保存します。
    • ^chatbot.conversation: すべての会話ペイロードを保存します。
    • ^chatbot.training.isupdated: トレーニングパイプラインを制御します。

    ほかのソリューションの製品

    全会話のレポートは作成しませんでしたが、私のグローバルグラフビューアを使用すれば、問題なく会話を追跡することができます。

    画像

    自分でアプリケーションを実行する

    前提条件

    • git
    • docker と docker-compose(docker のメモリ設定を 4 GB 以上に増加してください)
    • 環境のターミナルへのアクセス

    手順

    docker-compose を使って、すべてのピースと構成を備えた 1 つの環境を簡単に起動できます。iris-python-covid19 フォルダに移動し、次のように入力します。

    $ docker compose build
    $ docker compose up
    

    コンテナの推定起動時間

    初回実行時は、画像や依存関係をダウンロードするためのインターネット接続に依存します。 15 分以上かかる場合は、おそらく何らかの問題が発生しているので、こちらにご連絡ください。 2 回目以降はパフォーマンスが向上し、2 分もかかりません。

    すべてが万全である場合

    しばらくしたら、ブラウザを開いて次のアドレスに移動します。

    トレーニングデータのフォーム

    http://localhost:8050/chatbot-training-data
    

    チャットボット

    http://localhost:8080
    

    IRIS 管理ポータルの確認

    この時点では、USER ネームスペースを使用しています。

    http://localhost:9092
    user: _SYSTEM
    pass: theansweris42
    

    この記事が役に立った場合、またはコンテンツを気に入った場合は、投票してください。

    このアプリケーションは、Open Exchange コンテストに現在参加中です。こちら(https://openexchange.intersystems.com/contest/current)から私のアプリケーション「iris-python-suite」に投票してください。

    0
    0 638