Skip to content

Commit

Permalink
#2696 - Document-level recommendations
Browse files Browse the repository at this point in the history
- Towards handling extraction as part of the suggestion supports
  • Loading branch information
reckart committed Dec 25, 2023
1 parent e790b90 commit afb315d
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 139 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import static de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordUserAction.REJECTED;

import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
Expand All @@ -33,7 +34,6 @@
import org.apache.uima.cas.CAS;
import org.apache.uima.cas.Feature;
import org.apache.uima.jcas.cas.AnnotationBase;
import org.apache.uima.jcas.cas.TOP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
Expand Down Expand Up @@ -280,26 +280,35 @@ public Optional<SuggestionRenderer> getRenderer()
return Optional.empty();
}

public static void extractSuggestion(ExtractionContext ctx, TOP predictedFS)
public static List<AnnotationSuggestion> extractSuggestions(ExtractionContext ctx)
{
var autoAcceptMode = getAutoAcceptMode(predictedFS, ctx.getModeFeature());
var labels = getPredictedLabels(predictedFS, ctx.getLabelFeature(), ctx.isMultiLabels());
var score = predictedFS.getDoubleValue(ctx.getScoreFeature());
var scoreExplanation = predictedFS.getStringValue(ctx.getScoreExplanationFeature());

for (var label : labels) {
var suggestion = MetadataSuggestion.builder() //
.withId(MetadataSuggestion.NEW_ID) //
.withGeneration(ctx.getGeneration()) //
.withRecommender(ctx.getRecommender()) //
.withDocument(ctx.getDocument()) //
.withLabel(label) //
.withUiLabel(label) //
.withScore(score) //
.withScoreExplanation(scoreExplanation) //
.withAutoAcceptMode(autoAcceptMode) //
.build();
ctx.getResult().add(suggestion);
var result = new ArrayList<AnnotationSuggestion>();
for (var predictedFS : ctx.getPredictionCas().select(ctx.getPredictedType())) {
if (!predictedFS.getBooleanValue(ctx.getPredictionFeature())) {
continue;
}

var autoAcceptMode = getAutoAcceptMode(predictedFS, ctx.getModeFeature());
var labels = getPredictedLabels(predictedFS, ctx.getLabelFeature(),
ctx.isMultiLabels());
var score = predictedFS.getDoubleValue(ctx.getScoreFeature());
var scoreExplanation = predictedFS.getStringValue(ctx.getScoreExplanationFeature());

for (var label : labels) {
var suggestion = MetadataSuggestion.builder() //
.withId(MetadataSuggestion.NEW_ID) //
.withGeneration(ctx.getGeneration()) //
.withRecommender(ctx.getRecommender()) //
.withDocument(ctx.getDocument()) //
.withLabel(label) //
.withUiLabel(label) //
.withScore(score) //
.withScoreExplanation(scoreExplanation) //
.withAutoAcceptMode(autoAcceptMode) //
.build();
result.add(suggestion);
}
}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,15 @@
import static de.tudarmstadt.ukp.inception.recommendation.api.RecommendationService.FEATURE_NAME_IS_PREDICTION;
import static de.tudarmstadt.ukp.inception.recommendation.api.RecommendationService.FEATURE_NAME_SCORE_EXPLANATION_SUFFIX;
import static de.tudarmstadt.ukp.inception.recommendation.api.RecommendationService.FEATURE_NAME_SCORE_SUFFIX;
import static de.tudarmstadt.ukp.inception.support.WebAnnoConst.FEAT_REL_SOURCE;
import static de.tudarmstadt.ukp.inception.support.WebAnnoConst.FEAT_REL_TARGET;
import static org.apache.uima.cas.CAS.TYPE_NAME_STRING_ARRAY;

import java.util.ArrayList;
import java.util.List;

import org.apache.uima.cas.CAS;
import org.apache.uima.cas.Feature;
import org.apache.uima.cas.Type;
import org.apache.uima.fit.util.CasUtil;

import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer;
import de.tudarmstadt.ukp.clarin.webanno.model.SourceDocument;
import de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion;
import de.tudarmstadt.ukp.inception.recommendation.api.model.Recommender;

public final class ExtractionContext
Expand All @@ -56,17 +50,13 @@ public final class ExtractionContext
private final Type predictedType;

private final Feature labelFeature;
private final Feature sourceFeature;
private final Feature targetFeature;
private final Feature scoreFeature;
private final Feature scoreExplanationFeature;
private final Feature modeFeature;
private final Feature predictionFeature;

private final boolean isMultiLabels;

private final List<AnnotationSuggestion> result;

public ExtractionContext(int aGeneration, Recommender aRecommender, SourceDocument aDocument,
CAS aOriginalCas, CAS aPredictionCas)
{
Expand All @@ -84,17 +74,13 @@ public ExtractionContext(int aGeneration, Recommender aRecommender, SourceDocume

predictedType = CasUtil.getType(aPredictionCas, typeName);
labelFeature = predictedType.getFeatureByBaseName(featureName);
sourceFeature = predictedType.getFeatureByBaseName(FEAT_REL_SOURCE);
targetFeature = predictedType.getFeatureByBaseName(FEAT_REL_TARGET);
scoreFeature = predictedType.getFeatureByBaseName(featureName + FEATURE_NAME_SCORE_SUFFIX);
scoreExplanationFeature = predictedType
.getFeatureByBaseName(featureName + FEATURE_NAME_SCORE_EXPLANATION_SUFFIX);
modeFeature = predictedType
.getFeatureByBaseName(featureName + FEATURE_NAME_AUTO_ACCEPT_MODE_SUFFIX);
predictionFeature = predictedType.getFeatureByBaseName(FEATURE_NAME_IS_PREDICTION);
isMultiLabels = TYPE_NAME_STRING_ARRAY.equals(labelFeature.getRange().getName());

result = new ArrayList<AnnotationSuggestion>();
}

public int getGeneration()
Expand Down Expand Up @@ -152,16 +138,6 @@ public Feature getLabelFeature()
return labelFeature;
}

public Feature getSourceFeature()
{
return sourceFeature;
}

public Feature getTargetFeature()
{
return targetFeature;
}

public Feature getScoreFeature()
{
return scoreFeature;
Expand All @@ -186,9 +162,4 @@ public boolean isMultiLabels()
{
return isMultiLabels;
}

public List<AnnotationSuggestion> getResult()
{
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
import org.apache.uima.cas.Type;
import org.apache.uima.cas.text.AnnotationFS;
import org.apache.uima.fit.util.CasUtil;
import org.apache.uima.jcas.cas.TOP;
import org.apache.uima.jcas.tcas.Annotation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -377,40 +376,52 @@ public Optional<SuggestionRenderer> getRenderer()
featureSupportRegistry));
}

public static void extractSuggestion(ExtractionContext ctx, TOP predictedFS)
public static List<AnnotationSuggestion> extractSuggestions(ExtractionContext ctx)
{
var autoAcceptMode = getAutoAcceptMode(predictedFS, ctx.getModeFeature());
var labels = getPredictedLabels(predictedFS, ctx.getLabelFeature(), ctx.isMultiLabels());
var score = predictedFS.getDoubleValue(ctx.getScoreFeature());
var scoreExplanation = predictedFS.getStringValue(ctx.getScoreExplanationFeature());

var source = (AnnotationFS) predictedFS.getFeatureValue(ctx.getSourceFeature());
var target = (AnnotationFS) predictedFS.getFeatureValue(ctx.getTargetFeature());

var originalSource = findEquivalentSpan(ctx.getOriginalCas(), source);
var originalTarget = findEquivalentSpan(ctx.getOriginalCas(), target);
if (originalSource.isEmpty() || originalTarget.isEmpty()) {
LOG.debug("Unable to find source or target of predicted relation in original CAS");
return;
}
// TODO Use adapter instead - once the method is no longer static
var sourceFeature = ctx.getPredictedType().getFeatureByBaseName(FEAT_REL_SOURCE);
var targetFeature = ctx.getPredictedType().getFeatureByBaseName(FEAT_REL_TARGET);

var result = new ArrayList<AnnotationSuggestion>();
for (var predictedFS : ctx.getPredictionCas().select(ctx.getPredictedType())) {
if (!predictedFS.getBooleanValue(ctx.getPredictionFeature())) {
continue;
}

var position = new RelationPosition(originalSource.get(), originalTarget.get());

for (var label : labels) {
var suggestion = RelationSuggestion.builder() //
.withId(RelationSuggestion.NEW_ID) //
.withGeneration(ctx.getGeneration()) //
.withRecommender(ctx.getRecommender()) //
.withDocumentName(ctx.getDocument().getName()) //
.withPosition(position) //
.withLabel(label) //
.withUiLabel(label) //
.withScore(score) //
.withScoreExplanation(scoreExplanation) //
.withAutoAcceptMode(autoAcceptMode) //
.build();
ctx.getResult().add(suggestion);
var source = (AnnotationFS) predictedFS.getFeatureValue(sourceFeature);
var target = (AnnotationFS) predictedFS.getFeatureValue(targetFeature);

var originalSource = findEquivalentSpan(ctx.getOriginalCas(), source);
var originalTarget = findEquivalentSpan(ctx.getOriginalCas(), target);
if (originalSource.isEmpty() || originalTarget.isEmpty()) {
LOG.debug("Unable to find source or target of predicted relation in original CAS");
continue;
}

var autoAcceptMode = getAutoAcceptMode(predictedFS, ctx.getModeFeature());
var labels = getPredictedLabels(predictedFS, ctx.getLabelFeature(),
ctx.isMultiLabels());
var score = predictedFS.getDoubleValue(ctx.getScoreFeature());
var scoreExplanation = predictedFS.getStringValue(ctx.getScoreExplanationFeature());
var position = new RelationPosition(originalSource.get(), originalTarget.get());

for (var label : labels) {
var suggestion = RelationSuggestion.builder() //
.withId(RelationSuggestion.NEW_ID) //
.withGeneration(ctx.getGeneration()) //
.withRecommender(ctx.getRecommender()) //
.withDocumentName(ctx.getDocument().getName()) //
.withPosition(position) //
.withLabel(label) //
.withUiLabel(label) //
.withScore(score) //
.withScoreExplanation(scoreExplanation) //
.withAutoAcceptMode(autoAcceptMode) //
.build();
result.add(suggestion);
}
}
return result;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,30 +40,19 @@ public static List<AnnotationSuggestion> extractSuggestions(int aGeneration, CAS
var ctx = new ExtractionContext(aGeneration, aRecommender, aDocument, aOriginalCas,
aPredictionCas);

for (var predictedFS : aPredictionCas.select(ctx.getPredictedType())) {
if (!predictedFS.getBooleanValue(ctx.getPredictionFeature())) {
continue;
}

switch (ctx.getLayer().getType()) {
case SpanLayerSupport.TYPE: {
SpanSuggestionSupport.extractSuggestion(ctx, predictedFS);
break;
}
case RelationLayerSupport.TYPE: {
RelationSuggestionSupport.extractSuggestion(ctx, predictedFS);
break;
}
case DocumentMetadataLayerSupport.TYPE: {
MetadataSuggestionSupport.extractSuggestion(ctx, predictedFS);
break;
}
default:
throw new IllegalStateException(
"Unsupported layer type [" + ctx.getLayer().getType() + "]");
}
switch (ctx.getLayer().getType()) {
case SpanLayerSupport.TYPE: {
return SpanSuggestionSupport.extractSuggestions(ctx);
}
case RelationLayerSupport.TYPE: {
return RelationSuggestionSupport.extractSuggestions(ctx);
}
case DocumentMetadataLayerSupport.TYPE: {
return MetadataSuggestionSupport.extractSuggestions(ctx);
}
default:
throw new IllegalStateException(
"Unsupported layer type [" + ctx.getLayer().getType() + "]");
}

return ctx.getResult();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
import org.apache.uima.cas.text.AnnotationFS;
import org.apache.uima.cas.text.AnnotationPredicates;
import org.apache.uima.fit.util.CasUtil;
import org.apache.uima.jcas.cas.TOP;
import org.apache.uima.jcas.tcas.Annotation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -460,42 +459,47 @@ public Optional<SuggestionRenderer> getRenderer()
featureSupportRegistry, recommenderProperties));
}

public static void extractSuggestion(ExtractionContext ctx, TOP predictedFS)
public static List<AnnotationSuggestion> extractSuggestions(ExtractionContext ctx)
{
var autoAcceptMode = getAutoAcceptMode(predictedFS, ctx.getModeFeature());
var labels = getPredictedLabels(predictedFS, ctx.getLabelFeature(), ctx.isMultiLabels());
var score = predictedFS.getDoubleValue(ctx.getScoreFeature());
var scoreExplanation = predictedFS.getStringValue(ctx.getScoreExplanationFeature());

var predictedAnnotation = (Annotation) predictedFS;
var targetOffsets = getOffsets(ctx.getLayer().getAnchoringMode(), ctx.getOriginalCas(),
predictedAnnotation);

if (targetOffsets.isEmpty()) {
LOG.debug("Prediction cannot be anchored to [{}]: {}",
ctx.getLayer().getAnchoringMode(), predictedAnnotation);
return;
}
var result = new ArrayList<AnnotationSuggestion>();
for (var predictedFS : ctx.getPredictionCas().<Annotation> select(ctx.getPredictedType())) {
if (!predictedFS.getBooleanValue(ctx.getPredictionFeature())) {
continue;
}

var offsets = targetOffsets.get();
var coveredText = ctx.getDocumentText().substring(offsets.getBegin(), offsets.getEnd());

for (var label : labels) {
var suggestion = SpanSuggestion.builder() //
.withId(RelationSuggestion.NEW_ID) //
.withGeneration(ctx.getGeneration()) //
.withRecommender(ctx.getRecommender()) //
.withDocument(ctx.getDocument()) //
.withPosition(offsets) //
.withCoveredText(coveredText) //
.withLabel(label) //
.withUiLabel(label) //
.withScore(score) //
.withScoreExplanation(scoreExplanation) //
.withAutoAcceptMode(autoAcceptMode) //
.build();
ctx.getResult().add(suggestion);
var anchoringMode = ctx.getLayer().getAnchoringMode();
var targetOffsets = getOffsets(anchoringMode, ctx.getOriginalCas(), predictedFS);
if (targetOffsets.isEmpty()) {
LOG.debug("Prediction cannot be anchored to [{}]: {}", anchoringMode, predictedFS);
continue;
}

var autoAcceptMode = getAutoAcceptMode(predictedFS, ctx.getModeFeature());
var labels = getPredictedLabels(predictedFS, ctx.getLabelFeature(),
ctx.isMultiLabels());
var score = predictedFS.getDoubleValue(ctx.getScoreFeature());
var scoreExplanation = predictedFS.getStringValue(ctx.getScoreExplanationFeature());
var offsets = targetOffsets.get();
var coveredText = ctx.getDocumentText().substring(offsets.getBegin(), offsets.getEnd());

for (var label : labels) {
var suggestion = SpanSuggestion.builder() //
.withId(RelationSuggestion.NEW_ID) //
.withGeneration(ctx.getGeneration()) //
.withRecommender(ctx.getRecommender()) //
.withDocument(ctx.getDocument()) //
.withPosition(offsets) //
.withCoveredText(coveredText) //
.withLabel(label) //
.withUiLabel(label) //
.withScore(score) //
.withScoreExplanation(scoreExplanation) //
.withAutoAcceptMode(autoAcceptMode) //
.build();
result.add(suggestion);
}
}
return result;
}

/**
Expand Down

0 comments on commit afb315d

Please sign in to comment.