#include %IKPublic
DomainCreateOrOpen
SET dname="mydomain"
IF (##class(%iKnow.Domain).NameIndexExists(dname))
{ WRITE "The ",dname," domain already exists",!
SET domoref=##class(%iKnow.Domain).NameIndexOpen(dname)
GOTO DeleteOldData }
ELSE
{ WRITE "The ",dname," domain does not exist",!
SET domoref=##class(%iKnow.Domain).%New(dname)
DO domoref.%Save()
WRITE "Created the ",dname," domain with domain ID ",domoref.Id,!
GOTO ListerAndLoader }
DeleteOldData
SET stat=domoref.DropData()
IF stat { WRITE "Deleted the data from the ",dname," domain",!!
GOTO ListerAndLoader }
ELSE { WRITE "DropData error ",$System.Status.DisplayError(stat)
QUIT}
ListerAndLoader
SET domId=domoref.Id
SET flister=##class(%iKnow.Source.SQL.Lister).%New(domId)
SET myloader=##class(%iKnow.Source.Loader).%New(domId)
QueryBuild
SET myquery="SELECT TOP 100 ID AS UniqueVal,Type,NarrativeFull FROM Aviation.Event"
SET idfld="UniqueVal"
SET grpfld="Type"
SET dataflds=$LB("NarrativeFull")
UseLister
SET stat=flister.AddListToBatch(myquery,idfld,grpfld,dataflds)
IF stat '= 1 {WRITE "The lister failed: ",$System.Status.DisplayError(stat) QUIT }
UseLoader
SET stat=myloader.ProcessBatch()
IF stat '= 1 {WRITE "The loader failed: ",$System.Status.DisplayError(stat) QUIT }
SourceCountQuery
SET totsrc = ##class(%iKnow.Queries.SourceAPI).GetCountByDomain(domId)
WRITE totsrc," total sources",!
SimiarSourcesQuery
SET engineents = $LB("engine","engine failure","engine power","loss of power","carburetor","crankshaft","piston")
DO ##class(%iKnow.Queries.SourceAPI).GetByEntities(.result,domId,engineents,1,totsrc)
SET i=1
WHILE $DATA(result(i)) {
SET src = $LISTTOSTRING(result(i),",",1)
SET srcId = $PIECE(src,",",1)
WRITE "Source ",srcId," contains an engine incident",!
DO ##class(%iKnow.Queries.SourceAPI).GetSimilar(.sim,domId,srcId,1,50,"",$$$SIMSRCSIMPLE,$LB("ent"))
SET j=1
WHILE $DATA(sim(j)) {
SET simlist=$LISTTOSTRING(sim(j))
IF $PIECE(simlist,",",8) > .33 {
WRITE " similar to source ",$PIECE(simlist,",",1),": "
WRITE $PIECE(simlist,",",3,8),! }
SET j=j+1 }
SET i=i+1 }
ソースの要約
NLP 意味分析エンジンは、最も関連性のある文を返すことでソース・テキストを要約できます。これは、ソース・テキストのコンテンツ全体に最も類似する文を選択して、それらの文をユーザが指定した数だけ、元の文の順序で返します。NLP はそれぞれの文について内部関連性スコアを計算して、関連性を判断します。ソース・テキストに何度も出現する概念を含む文は、ソース・テキストに一度しか出現しない概念を含む文よりは、要約に含まれる可能性が高くなります。NLP は、各概念の総合的な頻度、ソースにおいて最も頻度の高い概念に対する各概念の類似性、および他の要因を考慮します。
ソースの要約は、ソースのロード時に構成で Summarize プロパティが 1 に設定された場合にのみ利用可能となります。既定の構成では、Summarize=1 が指定されます。
したがって、要約の正確さは、以下の 2 つの要因によって左右されます。
-
ソース・テキストは、意味のある頻度分析が可能な十分な大きさであることが必要ですが、大き過ぎることも禁物です。NLP の要約は、長い章や項目のテキストで最も効果を発揮します。本一冊分のテキストは、章ごとに要約する必要があります。
-
ユーザが読んで理解できる要約テキストが返される文によって構成されるように、要約内の文の数は元のテキストの十分大きなサブセットであることが必要です。要約割合の最小値は、25 % ~ 33 % となり、テキストの内容により異なります。
NLP には、以下の 3 つの要約メソッドが用意されています。
-
GetSummary()Opens in a new tab では、要約テキストの各文を個々の結果として返します。この文の ID は、返された各文の先頭要素として返されます。
-
GetSummaryDirect()Opens in a new tab では、要約テキストを 1 つの文字列として返します。既定では、この文字列内の文は省略記号 (空白スペース、3 つのピリオド、空白スペース) によって区切られます。例えば、“This is sentence one. ... This is sentence two.” のようになります。必要に応じて、別の文区切りを指定できます。このメソッドでは、複数の文を 1 つの文字列に連結するため、InterSystems IRIS の最大文字列長より長い文字列の作成を試みる場合があります。最大文字列長に達すると、InterSystems IRIS では、このメソッドの isTruncated というブーリアン出力パラメータが 1 に設定され、残りのテキストが切り捨てられます。
-
GetSummaryForText()Opens in a new tab では、特定のソース ID を指定するのではなく、ユーザ指定文字列の要約を直接編集する方式をサポートしています。
NLP が何を文と判断するかについての詳細は、“コンセプトの概要” の章の "NLP が識別する論理テキスト・ユニット" を参照してください。
以下の例では、101 以上の文を含むものを見つけるまで、ドメイン内のソース・テキストをチェックします。次に、GetSummary() を使用して、そのソースを元の文の半分に要約します。
#include %IKPublic
DomainCreateOrOpen
SET dname="mydomain"
IF (##class(%iKnow.Domain).NameIndexExists(dname))
{ WRITE "The ",dname," domain already exists",!
SET domoref=##class(%iKnow.Domain).NameIndexOpen(dname)
GOTO DeleteOldData }
ELSE
{ WRITE "The ",dname," domain does not exist",!
SET domoref=##class(%iKnow.Domain).%New(dname)
DO domoref.%Save()
WRITE "Created the ",dname," domain with domain ID ",domoref.Id,!
GOTO ListerAndLoader }
DeleteOldData
SET stat=domoref.DropData()
IF stat { WRITE "Deleted the data from the ",dname," domain",!!
GOTO ListerAndLoader }
ELSE { WRITE "DropData error ",$System.Status.DisplayError(stat)
QUIT}
ListerAndLoader
SET domId=domoref.Id
SET flister=##class(%iKnow.Source.SQL.Lister).%New(domId)
SET myloader=##class(%iKnow.Source.Loader).%New(domId)
QueryBuild
SET myquery="SELECT TOP 100 ID AS UniqueVal,Type,NarrativeFull FROM Aviation.Event"
SET idfld="UniqueVal"
SET grpfld="Type"
SET dataflds=$LB("NarrativeFull")
UseLister
SET stat=flister.AddListToBatch(myquery,idfld,grpfld,dataflds)
IF stat '= 1 {WRITE "The lister failed: ",$System.Status.DisplayError(stat) QUIT }
UseLoader
SET stat=myloader.ProcessBatch()
IF stat '= 1 {WRITE "The loader failed: ",$System.Status.DisplayError(stat) QUIT }
SourceSentenceTotals
SET numSrcD=##class(%iKnow.Queries.SourceAPI).GetCountByDomain(domId)
WRITE "The domain contains ",numSrcD," sources",!
SET numSentD=##class(%iKnow.Queries.SentenceAPI).GetCountByDomain(domId)
WRITE "These sources contain ",numSentD," sentences",!!
DO ##class(%iKnow.Queries.SourceAPI).GetByDomain(.result,domId)
SentenceCounts
FOR i=1:1:numSrcD {
SET srcId = $LISTGET(result(i),1)
SET extId = $LISTGET(result(i),2)
SET fullref = $PIECE(extId,":",3,4)
SET fname = $PIECE(fullref,"\",$LENGTH(extId,"\"))
SET numSentS = ##class(%iKnow.Queries.SentenceAPI).GetCountBySource(domId,result(i))
IF numSentS > 100 {WRITE fname," has ",numSentS," sentences",!
GOTO SummarizeASource }
}
QUIT
SummarizeASource
SET sumlen=$NUMBER(numSentS/2,0)
WRITE "total sentences=",numSentS," summary=",sumlen," sentences",!!
DO ##class(%iKnow.Queries.SourceAPI).GetSummary(.sumresult,domId,srcId,sumlen)
FOR j=1:1:sumlen { WRITE "[S",j,"]: ",$LISTGET(sumresult(j),2),! }
WRITE !,"END OF ",fname," SUMMARY",!!
QUIT
$NUMBER は、要約文の指定数を確実に整数にするために使用されています。$LISTGET は、文 ID を削除して、文テキストだけを返すために使用されています。
以下の例では、GetSummaryDirect() を使用して、連結された 1 つの文字列として同じ要約が返されます。次に、$EXTRACT を使用して、その文字列を以下のように表示するために、38 文字の行に分割します。
#include %IKPublic
DomainCreateOrOpen
SET dname="mydomain"
IF (##class(%iKnow.Domain).NameIndexExists(dname))
{ WRITE "The ",dname," domain already exists",!
SET domoref=##class(%iKnow.Domain).NameIndexOpen(dname)
GOTO DeleteOldData }
ELSE
{ WRITE "The ",dname," domain does not exist",!
SET domoref=##class(%iKnow.Domain).%New(dname)
DO domoref.%Save()
WRITE "Created the ",dname," domain with domain ID ",domoref.Id,!
GOTO ListerAndLoader }
DeleteOldData
SET stat=domoref.DropData()
IF stat { WRITE "Deleted the data from the ",dname," domain",!!
GOTO ListerAndLoader }
ELSE { WRITE "DropData error ",$System.Status.DisplayError(stat)
QUIT}
ListerAndLoader
SET domId=domoref.Id
SET flister=##class(%iKnow.Source.SQL.Lister).%New(domId)
SET myloader=##class(%iKnow.Source.Loader).%New(domId)
QueryBuild
SET myquery="SELECT TOP 100 ID AS UniqueVal,Type,NarrativeFull FROM Aviation.Event"
SET idfld="UniqueVal"
SET grpfld="Type"
SET dataflds=$LB("NarrativeFull")
UseLister
SET stat=flister.AddListToBatch(myquery,idfld,grpfld,dataflds)
IF stat '= 1 {WRITE "The lister failed: ",$System.Status.DisplayError(stat) QUIT }
UseLoader
SET stat=myloader.ProcessBatch()
IF stat '= 1 {WRITE "The loader failed: ",$System.Status.DisplayError(stat) QUIT }
SourceSentenceTotals
SET numSrcD=##class(%iKnow.Queries.SourceAPI).GetCountByDomain(domId)
WRITE "The domain contains ",numSrcD," sources",!
SET numSentD=##class(%iKnow.Queries.SentenceAPI).GetCountByDomain(domId)
WRITE "These sources contain ",numSentD," sentences",!!
DO ##class(%iKnow.Queries.SourceAPI).GetByDomain(.result,domId)
SentenceCounts
FOR i=1:1:numSrcD {
SET srcId = $LISTGET(result(i),1)
SET extId = $LISTGET(result(i),2)
SET fullref = $PIECE(extId,":",3,4)
SET fname = $PIECE(fullref,"\",$LENGTH(extId,"\"))
SET numSentS = ##class(%iKnow.Queries.SentenceAPI).GetCountBySource(domId,result(i))
IF numSentS > 100 {WRITE fname," has ",numSentS," sentences",!
GOTO SummarizeASource }
}
QUIT
SummarizeASource
SET sumlen=$NUMBER(numSentS/2,0)
WRITE "total sentences=",numSentS," summary=",sumlen," sentences",!!
SET summary = ##class(%iKnow.Queries.SourceAPI).GetSummaryDirect(domId,srcId,sumlen)
FormatSummaryDisplay
SET x=1
SET totlines=$LENGTH(summary)/38
FOR i=1:1:totlines {
WRITE $EXTRACT(summary,x,x+38),!
SET x=x+39 }
WRITE !,"END OF ",fname," SUMMARY"
カスタム・サマリ
NLP では、summaryConfig パラメータ文字列を指定することで、ソースのカスタム・サマリを生成できます。カスタム・サマリは、NLP で生成されたサマリの内容を自身のニーズに合わせて調整したいユーザのために提供されています。カスタム・サマリにより、文の要約への絶対的な包含、優先的な包含、または文の要約からの絶対的な除外が可能になります。例えば、タイトル、署名、著作権、抜粋、または要旨など常に同じ位置に現れるソースの標準コンポーネントを包含または除外することができます。また、指定した単語を含む文を、絶対的または優先的に包含/除外することもできます。
ソースの要約処理では、始めに各文に数値的な要約重要度が付与された後、最も重要度の高い文の適切な番号を選択することにより、要約を作成します。要約メソッドに対して summaryConfig パラメータを指定することで、このランキングを左右することもできます。
summaryConfig パラメータの値は、1 つ以上の仕様から成る文字列となります。各仕様は垂直バー ( | ) で仕切られた 3 つの要素から成ります。例えば、"s|2|false" となります。垂直バー ( | ) を使用して、複数の仕様を連結できます。例えば、"s|1|true|s|2|false" となります。summaryConfig パラメータの既定値は空の文字列です。
以下に従って、要約を構成することで、文を選択することができます。
連結演算により、複数の要約のカスタマイズを指定することができます。以下はその例です。"s|1|true|s|2|false|w|surgery|3|w|hypnosis|false" は、常に最初の文を含め、2 番目の文は除外し、単語 “surgery“ を含む文はすべて要約重要度を増やし、単語 “hypnosis“ を含む文はすべて除外するという意味になります。
したがって、特定の単語または文により高い (またはより低い) 重要性を与えることができます。summaryConfig の複数の仕様によって影響を受ける文の重要度は、カスタム・サマリ・アルゴリズムにより解決します。また、このアルゴリズムは、同じ文に適用する仕様の間で重複がある場合に適用します。
-
文 (s) の仕様と単語 (w) の仕様の間で重複がある場合、文の仕様が優先します。
-
2 つの s の仕様または 2 つの w の仕様に関して、包含 (true) と除外 (false) の間で重複がある場合、包含の仕様が優先します。
-
特定の要約の長さと、含める必要のある文の数または除外する必要のある文の数の間で重複がある場合、要約の長さは無視されます。
カスタム・サマリのオプションは、%iKnow.Queries.SourceAPI.GetSummary()Opens in a new tab および %iKnow.Queries.SourceAPI.GetSummaryForText()Opens in a new tab メソッドの summaryConfig パラメータにより設定できます。