【问题标题】:How to convert custom annotations to UIMA CAS structures and serialize them to XMI如何将自定义注解转换为 UIMA CAS 结构并将其序列化为 XMI
【发布时间】:2015-03-05 22:23:33
【问题描述】:

我在将自定义注释文档转换为 UIMA CAS 并将它们序列化为 XMI 以便通过 UIMA 注释查看器 GUI 查看注释时遇到问题。

我使用 uimaFIT 来构建我的组件,因为它更易于控制、测试和调试。管道由 3 个组件构成:

  • CollectionReader 组件读取带有原始文本的文件。
  • Annotator 用于将注释从自定义文档转换为 UIMA 注释的组件
  • CasConsumer 将 CAS 序列化为 XMI 的组件

我的管道工作并在最后输出 XMI 文件,但没有注释。我不太清楚 CAS 对象是如何在组件之间传递的。注释器逻辑包括对某些端点进行 RESTful 调用,并使用我尝试转换注释模型的服务提供的客户端 SDK。 Annotator 组件的转换逻辑部分如下所示:

public class CustomDocumentToUimaCasConverter implements UimaCasConverter {
    private TypeSystemDescription tsd;

    private AnnotatedDocument startDocument;

    private ArrayFS annotationFeatureStructures;

    private int featureStructureArrayCapacity;

    public AnnotatedDocument getStartDocument() {
        return startDocument;
    }

    public CustomDocumentToUimaCasConverter(AnnotatedDocument startDocument) {
        try {
            this.tsd = TypeSystemDescriptionFactory.createTypeSystemDescription();
        } catch (ResourceInitializationException e) {
            LOG.error("Error when creating default type system", e);
        }
        this.startDocument = startDocument;
    }


    public TypeSystemDescription getTypeSystemDescription() {
        return this.tsd;
    }

    @Override
    public void convertAnnotations(CAS cas) {
        Map<String, List<Annotation>> entities = this.startDocument.entities;
        int featureStructureArrayIndex = 0;

        inferCasTypeSystem(entities.keySet());
        try {
            /*
             * This is a hack allowing the CAS object to have an updated type system.
             * We are creating a new CAS by passing the new TypeSystemDescription which actually
             * should have been updated by an internal call of typeSystemInit(cas.getTypeSystem())
             * originally part of the CasInitializer interface that is now deprecated and the CollectionReader
             * is calling it internally in its implementation. The problem consists in the fact that now the
             * the typeSystemInit method of the CasInitializer_ImplBase has an empty implementation and
             * nothing changes!
             */
            LOG.info("Creating new CAS with updated typesystem...");
            cas = CasCreationUtils.createCas(tsd, null, null);
        } catch (ResourceInitializationException e) {
            LOG.info("Error creating new CAS!", e);
        }

        TypeSystem typeSystem = cas.getTypeSystem();
        this.featureStructureArrayCapacity = entities.size();
        this.annotationFeatureStructures = cas.createArrayFS(featureStructureArrayCapacity);

        for (Map.Entry<String, List<Annotation>> entityEntry : entities.entrySet()) {
            String annotationName = entityEntry.getKey();
            annotationName = UIMA_ANNOTATION_TYPES_PACKAGE + removeDashes(annotationName);
            Type type = typeSystem.getType(annotationName);

            List<Annotation> annotations = entityEntry.getValue();
            LOG.info("Get Type -> " + type);
            for (Annotation ann : annotations) {
                AnnotationFS afs = cas.createAnnotation(type, (int) ann.startOffset, (int) ann.endOffset);
                cas.addFsToIndexes(afs);
                if (featureStructureArrayIndex + 1 == featureStructureArrayCapacity) {
                    resizeArrayFS(featureStructureArrayCapacity * 2, annotationFeatureStructures, cas);
                }
                annotationFeatureStructures.set(featureStructureArrayIndex++, afs);
            }
        }
        cas.removeFsFromIndexes(annotationFeatureStructures);
        cas.addFsToIndexes(annotationFeatureStructures);
    }

    @Override
    public void inferCasTypeSystem(Iterable<String> originalTypes) {
        for (String typeName : originalTypes) {
            //UIMA Annotations are not allowed to contain dashes
            typeName = removeDashes(typeName);
            tsd.addType(UIMA_ANNOTATION_TYPES_PACKAGE + typeName,
                    "Automatically generated type for " + typeName, "uima.tcas.Annotation");
            LOG.info("Inserted new type -> " + typeName);
        }
    }

    /**
     * Removes dashes from UIMA Annotations because they are not allowed to contain dashes.
     *
     * @param typeName the annotation name of the current annotation of the source document
     * @return the transformed annotation name suited for the UIMA typesystem
     */
    private String removeDashes(String typeName) {
        if (typeName.contains("-")) {
            typeName = typeName.replaceAll("-", "_");
        }
        return typeName;
    }

    @Override
    public void setSourceDocumentText(CAS cas) {
        cas.setSofaDataString(startDocument.text, "text/plain");
    }

    private void resizeArrayFS(int newCapacity, ArrayFS originalArray, CAS cas) {
        ArrayFS biggerArrayFS = cas.createArrayFS(newCapacity);
        biggerArrayFS.copyFromArray(originalArray.toArray(), 0, 0, originalArray.size());
        this.annotationFeatureStructures = biggerArrayFS;
        this.featureStructureArrayCapacity = annotationFeatureStructures.size();
    }
}

` 如果有人处理过向 UIMA 类型的注释转换,我将不胜感激。

【问题讨论】:

  • 自定义注释文档:这些是 UIMA 注释吗?
  • 不,他们不是。 AnnotatedDocumentMap&lt;String, List&lt;Annotation&gt;&gt; 是我正在使用的服务的客户端 SDK 的对象部分。服务输出门 json,但我直接使用解析结果。

标签: java rest annotations nlp uima


【解决方案1】:

我认为你对 CAS 和 Annotations 的理解可能是错误的:

来自

* This is a hack allowing the CAS object to have an updated type system.

 LOG.info("Creating new CAS with updated typesystem...");
            cas = CasCreationUtils.createCas(tsd, null, null);

我了解到您尝试在 Annotator 的 process() 方法中创建一个新的 CAS(我假设您发布的代码在那里执行)。除非你正在实现一个 CAS 乘数,否则这不是实现它的方法。通常,collectionreader 提取原始数据并在其 getNext() 方法中为您创建一个 CAS。此 CAS 会在整个 UIMA 管道中传递,您只需向其添加 UIMA 注释即可。

对于您要添加的每个注释,UIMA 应该知道类型系统。如果您使用 JCasGen 及其生成的代码,这应该不是问题。确保可以自动检测您的类型,如下所述:http://uima.apache.org/d/uimafit-current/tools.uimafit.book.html#d5e531)。

这允许您使用 Java 对象来实例化注解,而不是使用低级 Fs 调用。以下 sn-p 在整个文档文本上添加注释。在文本中的标记上添加迭代逻辑和它们摄取的(非 UIMA)注释(使用您的 Web 服务)应该是微不足道的。

@Override
public void process(JCas aJCas) throws AnalysisEngineProcessException {
    String text = aJCas.getDocumentText();
    SomeAnnotation a = new SomeAnnotation(aJCas);
    // set the annotation properties
    // for each property, JCasGen should have
    // generated a setter
    a.setSomePropertyValue(someValue);
    // add your annotation to the indexes
    a.setBegin(0);
    a.setEnd(text.length());
    a.addToIndexes(aJCas);
}

为了避免混淆开始和结束字符串索引,我建议您使用一些 Token 注释(来自 DKPro Core,例如:https://dkpro.github.io/dkpro-core/),您可以将其用作自定义注释的锚点。

【讨论】:

  • 好吧,我的用例有点不同,因为在进行安静的调用之前我不知道注释。我完成了新 CAS 的整个创建,以便能够为当前文档设置适当的类型系统。我知道最好预先定义您的类型,但我希望能够灵活地处理注释转换,即使服务添加了新注释或它们会及时更改。对于这种情况,您有什么建议?
  • 我看到的问题是 UIMA 需要在运行时了解您的类型系统。例如,当向 UIMA-AS 集群添加新的处理节点时,您不能引入新类型(据我所知),即可以,但 UIMA 不会选择它。如果您希望注释转换灵活,可以使用 uimaFIT @ExternalResource (uima.apache.org/d/uimafit-current/…)。这允许您注入转换,而无需重写注释器。当 RESTful 服务发生变化时,唯一需要重写的类就是工厂类。
猜你喜欢
  • 2021-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-25
  • 2017-08-04
相关资源
最近更新 更多