0. 引言
这篇文章会介绍 Oracle Text 索引,旨在引导您了解创建文本索引、执行文本查询和维护文本索引的基础知识。
Oracle Text 为 Oracle 数据库提供全文索引功能,允许您通过指定内容中的单词、短语或其他文本模式来搜索文本内容(例如 VARCHAR2 或 CLOB 数据)。
Oracle Text 是数据库的标准组件,允许您在 Oracle 数据库中的文本数据中进行快速全文搜索。例如,它可以让您在地址字段中查找拼写错误的单词,或者获取包含特定短语的 Microsoft Word 文档列表。
虽然表面上它类似于 LIKE 运算符的索引版本,但存在许多差异。
Oracle Text 对数据库中的文本内容创建基于单词(word-based)的索引。该内容的范围可以从 VARCHAR2 列中的几个单词到存储在 BLOB 列中的多章节 PDF 文档(甚至存储在文件系统外部、URL 或云存储上)。
这篇文章主要面向开发人员和 DBA,内容涵盖创建索引、基本搜索功能和索引维护。
1. 整体流程
整体流程如下,
我们将创建一个名为“user_data”的简单表,其中包含客户信息。其中包括记录 ID 的数字列、客户名称的 VARCHAR2 列、订单金额的数字列以及销售代表所做的任何注释的 VARCHAR2 列。
我们将填充该表,然后在注释列上创建一个文本索引。
然后,我们将使用 Oracle Text CONTAINS 运算符完成各种类型的查询。我们还将在注释列上显示一些带有全文搜索的混合查询,并在其他关系列上显示附加过滤器。
接下来,我们将了解如何同步和优化 Oracle Text 索引。
最后,我们将了解如何在对象存储上索引二进制文件,例如 Microsoft Word 或 PDF 文件。
2. 创建索引
2-1. 创建一个简单的表
我们将创建一个简单的表来保存模拟用户销售记录。它包含一个用于记录 ID 的数字列、一个用于客户名称的 varchar 列、另一个用于销售额的数字列以及另一个用于注释的 varchar 列。将以下内容复制到“工作表”区域,然后按“运行语句”按钮:
1 2 3 4 5 6 | create table user_data (id number, name varchar2(100), amount number(17,2), note varchar2(2000) ) |
插入几行数据,
1 2 3 4 5 6 7 | insert into user_data select 1, 'John Smith' , 123.45, 'First order from John Smith.' from dual union select 2, 'Mary Poppins' , 67.89, 'First ever order from Marie Poppins.' from dual union select 3, 'John Smith' , 99.45, 'Second order from Johnny Smith.' from dual commit ; |
检查表中是否有数据,
1 | select * from user_data |
2-2. 创建文本索引
文本索引是域索引(domain index)的一个示例。域索引是特定类型数据(或“域”)的专门索引。为了告诉内核要创建什么类型的索引,我们使用特殊语法“INDEXTYPE IS …”。最常见的文本索引类型,也是我们在这里使用的,是 CONTEXT 索引类型。
复制并运行以下 SQL,这将在表的 TEXT 列上创建索引。
1 | create index myindex on user_data(note) indextype is ctxsys.context |
检查数据库视图中的索引,
1 2 | select index_name, index_type, status from user_indexes where index_name = 'MYINDEX' |
这样就有了您的索引,index_type 为“DOMAIN”,状态为“VALID”。文本索引必须有效才能使用。在大型表上创建的索引可能会显示为 INPROGRS,这意味着索引创建正在进行中,但尚未准备好使用。
我们还可以在“文本数据字典”中查找。这是用户 CTXSYS 拥有的一组视图,专门用于 Oracle Text 索引。这些视图均以“CTX_”为前缀,所有用户都可以查看。运行以下命令:
1 | select idx_name, idx_table, idx_text_name from ctx_user_indexes |
这告诉我们索引 MYINDEX 是在表 USER_DATA 的“NOTE”列上创建的。
2-3. 查看创建的基础表
文本索引作为一组基础表来实现。它们通常采用 DR 形式,其中后缀表示表的特定类型。通常不需要知道这些索引中有什么,但其中一个(“dollar I”表)特别有趣。
注意:子表列表将随数据库版本和所选索引选项的不同而变化。您可能会看到与此处显示的列表不同的列表。
您应该看到列出了几个表。单击旁边的三角形,打开 DR M Y I N D E X MYINDEX MYINDEXI 的表定义。
我们看到一个列列表。我们主要感兴趣的是 TOKEN_TEXT。
我们之前讨论了 Oracle Text 如何使用“基于单词”的索引。更准确地说,它使用“基于令牌”的索引,因为令牌不一定是单词(尽管通常是)。 “$I”表包含所有索引标记的列表,我们可以使用以下查询查看它们:
1 | select token_text from dr$myindex$i |
注意到列表中的任何内容了吗?文本中并未出现所有单词 – 缺少“from”。这是因为它被指定为“停用词” – 在搜索中不是很有用的常见词,但可能会占用索引中的大量空间。默认情况下,我们不会对它们建立索引 – 尽管使用高级选项,我们可以告诉系统对所有单词建立索引,或者提供我们不想索引的单词的“自定义非索引字表”。默认的停用词列表将随语言的不同而变化,并且取决于数据库的默认语言设置(自治数据库始终为英语)。您可以按照此处的示例自定义停用词列表:创建停用词列表和添加停用词。
目前我们不需要了解任何有关底层索引表的信息。但查看已索引的单词很有用,并且在尝试找出特定查询为何如此运行时,有时值得参考此列表(下一步将介绍查询)。
3. 运行查询
我们将探讨用于查询 Oracle Text 索引的 CONTAINS 运算符。
在这个章节,您将:
- 探索 CONTAINS 文本查询运算符
- 查看各种基本文本搜索
- 了解 SCORE() 运算符如何帮助您对查询结果进行排名
3-1. 运行文本查询
首先熟悉 USER_DATA 中包含的文本。
1 | select * from user_data |
3-2. CONTAINS 运算符
要搜索 Oracle Text CONTEXT 索引,必须使用 CONTAINS 运算符。 CONTAINS 特定于该类型的索引。与“普通”索引不同,无论有没有索引,您都无法获得相同的结果。如果不存在 CONTEXT 索引,CONTAINS 根本不起作用。
CONTAINS 是一个返回数字的函数。它几乎总是以 WHERE CONTAINS(…) > 0 的形式使用。如果返回值大于零,则该行有匹配项,如果为零则没有匹配项。
CONTAINS 需要两个或三个参数。第三个是可选的,我们稍后再讨论。两个必需的参数是:
- 要搜索的列的名称
- 要搜索的字符串值。该字符串可以是文字字符串,也可以是任何计算结果为字符串的字符串(VARCHAR2 或 CLOB)。
让我们尝试一个简单的例子。我们将查找单词“John”:
1 2 | select * from user_data where contains ( note, 'john' ) > 0 |
请注意,我们找到了包含单词“John”的一行。但是,我们没有找到包含“Johnny”的行。这说明了 Oracle Text 搜索与简单的 LIKE 搜索(例如 WHERE TEXT LIKE ‘%John%’)之间的众多差异之一。 LIKE 执行子字符串搜索,而 CONTAINS 则(默认情况下)查找整个单词。
您还可以尝试搜索大写的 JOHN。你会得到相同的结果。与 LIKE 搜索不同,CONTAINS 搜索(至少对于英文索引)不区分大小写。
3-3. 混合查询
CONTAINS 是一个 SQL 运算符。当然,您可以将其与任何其他 WHERE 子句结合起来。例如,我们可以查找 AMOUNT 值小于 100 的单词“Smith”。让我们尝试一下:
1 2 | select * from user_data where amount 0 |
3-4. OR 查询
CONTAINS 的搜索字符串参数有自己的语法,具有各种内部运算符,例如 AND、OR、NEAR 等。我们在这里仅展示一个示例,有关更多信息,您应该参阅文档。
之前,我们搜索“John”但没有找到“Johnny”。让我们搜索一下:
1 2 | select * from user_data where contains ( note, 'john OR johnny' ) > 0 |
果然,现在我们两者都找到了。
3-5. 通配符
运行先前搜索的另一种方法是使用通配符 %。与标准 SQL 一样,百分号 % 匹配任何字符串,下划线 _ 字符匹配任何单个字符。
因此 john% 将匹配“john”、“johnny”、“johnnie”、“johnston”等。 l_se 将匹配“lose”,但不匹配“loose”。
由于通配符仅适用于索引单词,因此它们永远不会匹配空格。因此 qui%step 将匹配“quickstep”,但不会匹配短语“quick step”。
我们来尝试一下:
1 2 | select * from user_data where contains ( note, 'john%' ) > 0 |
3-6. 短语搜索
如果您想在同一文档中查找两个单词,您可以执行 AND 搜索,类似于上面的 OR 搜索。如果您想一起查找两个单词,只需将它们作为短语输入即可。无需添加引号或任何内容,两个单词一起自动构成短语搜索,并且仅当它们一起出现在索引文本中时才会匹配。
1 2 | select * from user_data where contains ( note, 'first order' ) > 0 |
Note that only matches the first row where the actual phrase “first order” appears, and not the other row where the two words appear, but not as a phrase.
3-7. 模糊搜索(Fuzzy searches)
如果您犯了错误或根本不记得确切的拼写,您可以进行模糊搜索。它不仅会找到原始搜索词,还会找到所有与其相似的搜索词。
1 2 | select * from user_data where contains ( note, 'fuzzy(popins)' ) > 0 |
请注意,搜索词“popins”存在拼写错误。但通过模糊搜索,它实际上找到了包含正确单词“Poppins”的结果。
3-8. 附近的搜索
您可以使用 NEAR 运算符查找彼此接近的单词。它将查找彼此指定距离内的单词。例如,以下查询未找到任何结果。因为“order”和“smith”之间有两个单词,但我们指定它们之间最多有 1 个单词。
1 2 | select * from user_data where contains ( note, 'near((order, smith), 1)' ) > 0 |
下一个查询找到结果,因为它正确指定了“order”和“smith”之间的距离 2。
1 2 | select * from user_data where contains ( note, 'near((order, smith), 2)' ) > 0 |
请注意,默认情况下,近运算符中的单词顺序并不重要,除非 ORDER 参数显式设置为 TRUE。但在短语搜索中,单词的顺序确实很重要。
You can find a list of query operators here: Contains Query Operators.
4. 维护索引
到目前为止,我们已经了解了如何创建和查询 Oracle Text 索引。这是 Oracle Text 的基础知识,但我们需要讨论索引维护下的几个主题。
默认情况下,Oracle Text 索引不是事务性的。索引表发生更改后,必须先同步索引,然后才能通过搜索找到新数据。
对 Oracle Text 索引进行多次更改后,由于索引碎片和垃圾(已删除)数据在索引中累积,其性能将达不到理想状态。为了让索引达到最佳状态,我们必须对其进行优化。
在本章节中,您将:
- 看到索引没有自动更新
- 了解如何手动或自动同步索引
- 看到索引随着时间的推移变得碎片化
- 了解如何优化索引
4-1. 同步
让我们向 USER_DATA 中插入一个新行。复制以下内容并单击“运行语句”按钮:
1 2 | insert into user_data values (4, 'Mike Smith' , 98.76, 'Third one from Mike Smith.' ); commit ; |
现在尝试查询刚刚插入的数据:
1 2 | select * from user_data where contains ( note, 'mike' ) > 0 |
当你运行它时,你将不会得到任何结果。请记住 CONTAINS 仅适用于 CONTEXT 索引。如果该索引不是最新的,那么结果也不会是最新的。为了获得正确的结果,我们必须同步索引。执行此操作的基本方法是调用 PL/SQL 过程 CTX_DDL.SYNC_INDEX(您的用户需要具有 CTXAPP 角色才能访问该过程,或者已被显式授予 EXECUTE ON CTXSYS.CTX_DDL)。索引名称作为参数传递给过程。
运行这个命令同步索引,
1 | execute ctx_ddl.sync_index ( 'myindex' ) |
现在再次尝试之前的“mike”查询,它将起作用。
4-2. 提交时自动同步
手动运行 SYNC_INDEX 非常高效,并且可以让您完全控制。但是,您可以让索引自动同步,方法是指定应在提交时同步,或者指定定期的时间段(例如每分钟)来执行同步。
首先删除当前索引:
1 | drop index myindex |
每当需要非默认索引行为时,我们都会对索引使用 PARAMETERS 子句。这里我们将指定 SYNC(ON COMMIT) 以使其在 COMMIT 时自动同步:
1 2 | create index myindex on user_data(note) indextype is ctxsys.context parameters ( 'sync (on commit)' ) |
现在我们将向表中添加一个新行并搜索它:
1 2 | insert into user_data values (5, 'Peter Williams' , 110.68, 'Canceled order from Peter Williams.' ); commit ; |
我们将找到新行,而无需调用 CTX_DDL.SYNC_INDEX。
1 2 | select * from user_data where contains ( note, 'williams' ) > 0 |
4-3. 按时间间隔自动同步
SYNC(ON COMMIT) 很方便,但在高事务率情况下并不理想。它可能会导致事务在等待上一个 SYNC 完成时被延迟。相反,您可以选择在特定时间段执行 SYNC。
该时间段越长(通常选择五分钟),您的索引需要优化的次数就越少。但是,如果您需要近乎实时的同步,则可以选择低至一秒的时间段。
时间间隔SYNC使用数据库调度程序,因此在19c及之前您必须具有CREATE JOB权限才能使用它。
删除现有索引:
1 | drop index myindex |
现在再次创建索引,但这次指定应每分钟同步一次。时间段的语法来自 DBMS_SCHEDULER。
1 2 | create index myindex on user_data(note) indextype is ctxsys.context parameters ( 'sync (every "freq=minutely; interval=1")' ) |
现在插入一个新行
1 2 | insert into user_data values (6, 'Paul Williams' , 77.36, 'Returned order from Paul Williams.' ); commit ; |
搜索新行。最初,您可能会发现它找不到新行,但继续重复查询,它会在一分钟内起作用。
1 2 | select * from user_data where contains (note, 'paul' ) > 0; |
4-4. 优化
检查“$I”表,现在我们已经完成了索引的更新,让我们再看一下 $I 表中的索引词列表。运行以下命令:
1 | select token_text from dr$myindex$i |
您应该看到现在有两个单词“order”和“williams”的条目。我们不会担心到底为什么(尽管请注意它们是在上次更新中使用的),但我们只是说这是索引碎片的一个示例。
优化索引,我们可以使用 ctx_ddl 包中的另一个 PL/SQL 命令来优化索引:ctx_ddl.optimize_index。这需要两个强制参数:索引名称和要执行的优化类型。常见值为“FULL”或“REBUILD”。我们将选择“FULL”:
1 | execute ctx_ddl.optimize_index( 'myindex' , 'FULL' ) |
现在再次尝试从 $I 表中进行先前的选择。现在只有一个“order”条目和一个“williams”条目 – 这些单词的索引信息已被压缩为每个单词的一行。
您现在应该在创建 Oracle Text 索引、针对这些索引运行基本查询以及维护这些索引方面具备良好的基础。
5. 对象存储上的索引文件
在之前的实验中,我们向您展示了如何对简单的 VARCHAR2 文本进行索引。但 Oracle Text 的能力远不止于此。例如,它可以自动识别和处理大约 150 种不同的二进制文件格式。有 PDF 文档吗?没问题。想要索引 Powerpoint 演示文稿中的所有文本吗?当然可以,为什么不呢?
Oracle Text 可以处理文件系统或 URL 上保存的文件,但对于本例,我们将把文件直接加载到数据库的 BLOB(二进制长对象)列中。
完成之前的所有实验后,此实验是可选的。您可以索引自己的文件,或使用我们提供的简单的 Microsoft Word 文件。
在本章节中,您将:
- 将文件复制到对象存储
- 创建“预授权请求”URL 来访问这些文件
- 使用 DBMS_CLOUD.GET_OBJECT 将文件加载到数据库中
- 创建一个首选项,告诉 Text 使用 AUTO_FILTER
索引文件并使用内容词进行搜索
5-1. 将文件加载到对象存储
转到 Oracle Cloud 中的主菜单(请注意,这与数据库操作的菜单不同 – 它可能在不同的选项卡中打开)。
打开“汉堡包”菜单,然后选择“存储”,然后选择“对象存储和归档存储”下的存储桶。
在“存储桶”页面中,从“搜索分区”框中选择您的根分区。
然后点击“创建存储桶”,您可以提供名称或仅保留默认名称。单击创建。
现在单击新创建的存储桶。
滚动到页面底部找到对象并单击上传。
在“上传对象”面板中,您可以使用文件选择器从计算机中选择某些 Office 或 PDF 文件,或将它们拖放到页面上。
如果您没有任何合适的文件,您可以从这里下载一个简单的 Microsoft Word 文档。
关于 PDF 文件的说明:Oracle Text 无法处理纯图像的 PDF 文件(即使它们是文本图像 – 我们没有 OCR 功能)。 PDF 文件必须嵌入文本。有时,PDF 文件会受到文本访问保护,或使用无法读取的特殊“位图”字体。不过,绝大多数 PDF 文件都可以使用。
选择文件后,单击“上传”按钮即可完成。然后单击“关闭”,您应该会看到存储桶中列出了您的文件。
为文件创建预验证请求 (PAR)。
对于存储桶中的每个文件,我们需要创建一个“预验证请求”。这是一个特殊的 URL,其中包含文件的嵌入式访问密钥。这意味着任何有权访问该 URL 的人都可以访问该文件,但实际上不可能猜测该 URL。
单击文件右侧的“三点”菜单,然后选择“创建预验证请求”。
在弹出的面板上,选择“对象”,然后单击“创建预验证请求”按钮。
您将看到一个“预验证请求详细信息”面板,单击“复制”按钮复制 URL,然后将其保存到文本文件以供以后使用。对每个要索引的文件重复此操作。
注意:不要担心可怕的“不会再显示”。您可以随时创建另一个 PAR。
5-2. 将文件加载到数据库中
在浏览器中打开“数据库操作”选项卡(或者如果需要,可以使用之前的说明重新打开它)并转到 SQL。
创建一个表来保存文件数据。运行以下语句:
1 | create table documents ( name varchar2(50), content blob) |
将文件从对象存储加载到表中。
对您存储的每个文件运行一次,替换为 PAR URL(您在上一步中保存的)和短名称或描述。不要忘记为每个文件指定不同的名称/描述。
1 2 3 4 5 6 | declare body blob; begin insert into documents values ( 'Hello World as an MS Word file' , body); end ; |
通过获取名称和 LOB 列的大小来检查文件是否已正确加载
1 | select name , dbms_lob.getlength(content) from documents |
5-3. 索引文档
创建过滤器首选项。
Oracle Text 很聪明地发现,如果它对 BLOB 列建立索引,那么它显然是在处理二进制文件,需要通过 AUTO_FILTER 才能被识别并转换为文本。所以实际上我们可以像以前一样创建一个简单的文本索引。但为了说明如何自定义索引选项,我们将向您展示如何创建一个首选项,明确告诉文本使用 AUTO_FILTER,覆盖其索引的数据类型的任何默认值。我们还将根据我们的偏好设置一个 TIMEOUT 属性,告诉它过滤任何特定文件的时间不要超过 10 秒。
我们需要创建一个首选项,然后设置该首选项的属性。这些都是使用名为 ctx_ddl 的包完成的,任何具有 CTXAPP 角色的用户都可以执行该包。由于这里有两个语句,因此使用“运行脚本”按钮运行它们是最简单的。或者,突出显示这两个语句并按“运行”。
1 2 | exec ctx_ddl.create_preference ( 'my_filter_pref' , 'AUTO_FILTER' ) exec ctx_ddl.set_attribute ( 'my_filter_pref' , 'TIMEOUT' , 10) |
(如果您需要再次运行该程序,您可以调用 ctx_ddl.drop_preference,仅将首选项名称作为参数)
使用我们的过滤器首选项创建索引。
对于任何具有非标准选项的索引,我们使用 PARAMETERS 子句(我们之前在 SYNC 选项中看到过它)。该子句采用单个字符串,该字符串主要是首选项类型和首选项名称的列表。在这里,我们的首选项类型是“filter”,首选项名称是“my_filter_pref”。如果我们想添加更多文件,我们还将包括同步(提交时)。
1 2 3 | create index documents_index on documents(content) indextype is ctxsys.context parameters ( 'filter my_filter_pref sync(on commit)' ) |
如果该语句出现任何问题 – 就像您拼写错误您的偏好一样,它可能会创建失败的索引。在创建新索引之前,您需要删除该索引。
5-4. 搜索文件
我们的 CONTAINS 将针对索引的 CONTENT 列运行,但由于它是二进制的,因此没有必要选择它,因此我们只需选择 NAME 列。如果您不为 HelloWorld 文档编制索引,则可以在此处替换为您自己的搜索字符串。
1 | select name from documents where contains (content, 'world' ) > 0 |
可选:获取片段(上下文中突出显示的搜索词)
我们无法读取表中的二进制文档,但我们可以让 Text 用它来做一些事情。 CTX_DOC 包有各种处理索引文档的过程。让我们看一下 CTX_DOC.SNIPPET,它获取搜索词周围的文档块。它通常从 PL/SQL 调用,但如果我们传入索引名称和我们正在查看的行的 ROWID 值,我们也可以从 SQL SELECT 查询调用它。我们还必须告诉它所使用的搜索词。所以我们得到:
1 | select name , ctx_doc.snippet( 'DOCUMENTS_INDEX' , rowid, 'world' ) from documents where contains (content, 'world' ) > 0 |
如果您正在搜索其他文档,请不要忘记将“world”更改两次。
CTX_DOC包含许多用于处理单个文档的函数。值得一看文档。
如果您已完成此可选模块,您就会知道 Oracle Text 可以处理的不仅仅是数据库中的短文本。为什么不尝试更多的文件呢?也许将“文档”文件夹中的所有文件加载到数据库中,最终,您将能够找到您多年前编写的难以捉摸的 Powerpoint,但不记得它的文件名。
6. (可选)情感分析
情绪分析可以回答“产品评论是正面还是负面?”等问题。或“客户满意还是不满意?”例如,从包含特定产品的多个评论的文档集中,您可以确定表明该产品是好还是坏的总体情绪。
Oracle Text 使用户能够使用经过训练以识别情感元数据的情感分类器对主题或文档执行情感分析。
Oracle Text 可以使用基本的内置“词袋”分类器(针对英文文本)执行情感分析。为了获得更好的结果或使用其他语言,您可以使用一组培训文档来训练您自己的分类器。
本章节将使用Oracle数据库创建一个分类器,并用它来分析一组有关相机评论的文档的情绪。
在本章节中,您将:
- 使用内置的默认分类器分析文档的情绪
- 使用单独的训练文档集训练自定义分类器
- 使用经过训练的分类器来分析文档
- 比较两种分析方法的准确性
6-1. 使用默认分类器
加载评论数据进行分析,这是您要分析的实际数据。在本例中,我们将创建一个“camera_review”表并将评论文本加载到该表中。
1 | create table camera_reviews(review_id number primary key , review_text varchar2(2000)) |
插入评论数据。您需要在运行之前选择所有行,或者使用“运行 SQL 脚本”按钮,
1 2 3 4 5 6 7 8 | insert into camera_reviews values (1, 'this camera is OK' ); insert into camera_reviews values (2, 'the camera is absolutely fantastic' ); insert into camera_reviews values (3, 'the camera is terrible' ); insert into camera_reviews values (4, 'another fantastic camera from Nikon' ); insert into camera_reviews values (5, 'What a terrible camera from Canon' ); insert into camera_reviews values (6, 'camera is not too bad, but ok for the price' ); insert into camera_reviews values (7, 'lens is not too bad, love the looks of this camera' ); insert into camera_reviews values (8, 'the Sony camera has a lot of new features, although a bit pricey' ); |
检查所有行是否已加载。您应该看到 8 行。
1 | exec ctx_ddl.create_preference( 'review_lexer' , 'AUTO_LEXER' ) |
使用我们刚刚创建的首选项和 NOPOPULATE 关键字创建评论数据的索引,
1 2 3 | create index camera_revidx on camera_reviews(review_text) indextype is ctxsys.context parameters ( 'lexer review_lexer NOPOPULATE' ); |
6-2. 运行情绪分析
情绪分析使用 PL/SQL 过程 CTX_DOC.SENTIMENT_AGGREGATE 逐行运行。返回一个数值,表示文档的情绪,范围在 -100 到 100 之间,其中 -100 最大为负面,0 为中性,100 最大为正面。该函数采用索引的名称和 TEXTKEY,TEXTKEY 可以是行的唯一键值,或者如果表没有唯一键,则为 ROWID 值(我们可以使用 CTX_DOC.SET_KEY_TYPE 在使用键和 rowids 之间进行交换。
因此,为了获取每个评论文本及其计算出的情绪,我们可以运行以下命令:
1 2 3 4 5 | select ctx_doc.sentiment_aggregate( index_name => 'camera_revidx' , textkey => review_id ) sentiment, review_text from camera_reviews; |
6-3. 使用经过训练的分类器
训练分类器涉及提供一组已知为正面、中性或负面的训练文档。此类文档可能已经过人工审核,或者您可能会使用用户提供的其他元数据(例如星级评定)。
训练使用称为支持向量机(SVM)的机器学习算法。
您提供的训练文档越多,分类器就越好。由于我们在这里只提供很少的示例文档,因此分类器将非常粗糙。
加载训练数据,我们将加载评论培训表。在下一步中,我们将为每条评论贴上相关情绪的标签。
首先,创建一个名为“training_camera”的表来保存训练数据,
1 | create table training_camera(train_id number primary key , train_text varchar2(2000)) |
将训练数据插入训练表,
1 2 3 4 5 6 | insert into training_camera values ( 1, 'this camera is OK' ); insert into training_camera values ( 2, 'the camera is absolutely fantastic' ); insert into training_camera values ( 3, 'the camera is terrible' ); insert into training_camera values ( 4, 'i love the lens, but overall ok camera' ); insert into training_camera values ( 5, 'the camera has mediocre lens, but a lot of nice features' ); commit ; |
用情感标记训练数据,我们使用一个单独的表来保存与“training_camera”表中每一行相关的情绪:
1 | create table training_category(doc_id number, category number, category_desc varchar2(100)) |
对于训练数据表中的每一行,我们必须插入一行来指示该行的类别。类别是代表中性、正面或负面的整数值,如下表所列。 “categoy_desc”列作为人类可读的注释包含在此处,在分类过程中既不是必需的,也不是使用的。
整数 | 意义 |
---|---|
0 | 中立 |
1 | 积极 |
2 | 负 |
鉴于此,我们可以如下创建类别行(您可能希望返回训练表以检查每行涉及的文本)
insert into training_category values( 1, 0, ‘neutral’);
insert into training_category values( 2, 1, ‘positive’);
insert into training_category values( 3, 2, ‘negative’);
insert into training_category values( 4, 0, ‘neutral’);
insert into training_category values( 5, 0, ‘neutral’);
6-4. 创建 SVM 情感分类器
1 | exec ctx_ddl.create_preference( 'classifier_camera' , 'SENTIMENT_CLASSIFIER' ) |
您可以选择设置分类器“classifier_camera”的属性。
1 2 | exec ctx_ddl.set_attribute( 'classifier_camera' , 'MAX_FEATURES' , '1000' ); exec ctx_ddl.set_attribute( 'classifier_camera' , 'NUM_ITERATIONS' , '600' ); |
6-5. 索引训练集
在训练表上创建索引。该索引仅用于其关联的元数据,因此可以使用“nopopulate”选项创建,并且速度非常快。对于经过训练的分类器,您确实需要使用 AUTO_LEXER,我们将允许它使用默认的英语词法分析器 (BASIC_LEXER)。
1 2 | create index training_idx on training_camera(train_text) indextype is ctxsys.context parameters ( 'nopopulate' ); |
6-6. 训练分类器
过程 SA_TRAIN_MODEL(SA 用于情感分析)获取有关训练和类别表(及其各个列)的信息,以及我们刚刚创建的索引和分类器首选项的名称。然后,这将生成一个模型,其名称在第一个参数中给出 – 在本例中为“my_clsfier”
1 2 3 4 5 6 7 8 9 10 11 | begin ctx_cls.sa_train_model ( clsfier_name => 'my_clsfier' , index_name => 'training_idx' , docid => 'train_id' , cattab => 'training_category' , catdocid => 'doc_id' , catid => 'category' , pref_name => 'classifier_camera' ); end ; |
6-7. 使用经过训练的分类器运行情感分析
运行情感分析的过程与上次非常相似,只是这次我们需要提供分类器名称,而不是允许其默认。
请记住,我们在camera_reviews 表上已经有一个“nopopulate”文本索引 – 如果我们在上一步中没有创建它,我们需要在使用新分类器之前在此处创建它。
1 2 3 4 5 6 | select ctx_doc.sentiment_aggregate( index_name => 'camera_revidx' , textkey => review_id, clsfier_name => 'my_clsfier' ) sentiment, review_text from camera_reviews; |
我们还可以在同一个查询中运行经过训练和未经训练的分类器,以比较两者的效率。当然,这是一个精心选择训练词的人为示例,但在现实世界中,假设训练集大小合理,您应该会看到经过训练的分类器具有明显更好的性能。
1 2 3 4 | select review_text, ctx_doc.sentiment_aggregate( 'camera_revidx' , review_id) as default_sentiment, ctx_doc.sentiment_aggregate( 'camera_revidx' , review_id, clsfier_name => 'my_clsfier' ) as trained_sentiment from camera_reviews order by trained_sentiment; |
You can find more details of sentiment analysis here: [Sentiment Analysis](You can find more details of sentiment analysis here: Sentiment Analysis.
).
refer: Full-Text Search in Oracle Database ShareStart
完结!