Steg 9 (R4): unified migrate-corpus.mjs --write over engineering/governance/ infrastructure/security. 327 filer mutert, verified=null, prosa byte-identisk (fra første ## seksjon), advisor urørt (0 endringer). To applier-fixes oppdaget under kjøring (TDD, RED→GREEN): - insertHeaderFields: anker faller nå tilbake når en meta-linje selv passerer 500B (2 filer pakket et avsnitt i **Status:** → Type/Source landet utenfor scan-vinduet, applierens post-write-assertion fanget + restaurerte). - normalizeStaleVerified: fjerner nå ALLE stale non-date **Verified:** i 500B-vinduet, inkl. stray body-dup rett under --- (9 mlops-genaiops-filer var ellers falskt "verified"/fresh, droppet fra worklist). Operatør-godkjent utvidelse av carve-out; kun stray metadata-linjer, aldri prosa. test-transform-criterion: precondition oppdatert til post-migrasjons-sannhet (fila bærer nå Source). Suite 728/728 grønn.
526 lines
18 KiB
Markdown
526 lines
18 KiB
Markdown
# Data Sampling and Labeling Strategies
|
|
|
|
**Last updated:** 2026-06-24
|
|
**Status:** GA
|
|
**Category:** Data Engineering for AI
|
|
**Type:** reference
|
|
**Source:** https://learn.microsoft.com/azure/machine-learning/how-to-label-data
|
|
|
|
---
|
|
|
|
## Innhold
|
|
|
|
- [Introduksjon](#introduksjon)
|
|
- [Stratified Sampling for Class Balance](#stratified-sampling-for-class-balance)
|
|
- [Active Learning and Uncertainty Sampling](#active-learning-and-uncertainty-sampling)
|
|
- [Crowdsourcing and Labeling Platforms](#crowdsourcing-and-labeling-platforms)
|
|
- [Quality Control and Inter-Rater Agreement](#quality-control-and-inter-rater-agreement)
|
|
- [Feedback Loops for Continuous Labeling](#feedback-loops-for-continuous-labeling)
|
|
- [Referanser](#referanser)
|
|
- [For Cosmo](#for-cosmo)
|
|
|
|
## Introduksjon
|
|
|
|
Kvaliteten pa treningsdata er den viktigste faktoren for ytelsen til ML-modeller. Effektiv datasampling sikrer at treningsdatasettet er representativt og balansert, mens systematisk datamerking (labeling) gir modellene de korrekte signalene a laere fra. Azure Machine Learning tilbyr en komplett plattform for datamerking med stotte for bade bilde- og tekstdata, inkludert ML-assistert merking som akselererer prosessen vesentlig.
|
|
|
|
For norsk offentlig sektor, der data ofte er ubalansert (f.eks. svaert fa svindeltilfeller vs. legitime transaksjoner, eller sjaeldne hendelser i trafikkdata), er stratifisert sampling og aktiv laering spesielt viktig. Riktig sampling reduserer merkebehovet med 50-80%, noe som sparer bade tid og kostnader i prosjekter med stramme budsjetter.
|
|
|
|
Denne referansen dekker hele livssyklusen fra datautvalg gjennom merkeprosesser til kvalitetskontroll, med fokus pa teknikker som er relevante for Microsoft AI-stakken og Azure Machine Learning.
|
|
|
|
---
|
|
|
|
## Stratified Sampling for Class Balance
|
|
|
|
### Problemet med ubalanserte datasett
|
|
|
|
| Scenario | Positiv klasse | Negativ klasse | Ubalanse-ratio |
|
|
|----------|---------------|----------------|----------------|
|
|
| Svindeldeteksjon | 0.1% svindel | 99.9% legitim | 1:1000 |
|
|
| Ulykkesprediksjon | 2% ulykker | 98% normal trafikk | 1:50 |
|
|
| Dokumentklassifisering | 5% sensitiv | 95% ikke-sensitiv | 1:19 |
|
|
| Feildeteksjon (IoT) | 0.5% feil | 99.5% normal | 1:200 |
|
|
|
|
### Stratifisert sampling i PySpark
|
|
|
|
```python
|
|
from pyspark.sql import functions as F
|
|
|
|
def stratified_sample(df, label_column, sample_fractions, seed=42):
|
|
"""
|
|
Utfor stratifisert sampling for a balansere klasser.
|
|
|
|
Args:
|
|
df: Input DataFrame
|
|
label_column: Kolonnen som inneholder klassen
|
|
sample_fractions: Dict med {klasse: samplingandel}
|
|
seed: Random seed for reproduserbarhet
|
|
"""
|
|
sampled = df.sampleBy(label_column, fractions=sample_fractions, seed=seed)
|
|
return sampled
|
|
|
|
# Eksempel: Balanser svindeldatasett
|
|
# Original: 99.9% legitim, 0.1% svindel
|
|
sample_fractions = {
|
|
"legitimate": 0.01, # Sample 1% av legitime (reduser fra 99.9k til ~1k)
|
|
"fraud": 1.0 # Behold alle svindeltilfeller (~100)
|
|
}
|
|
|
|
balanced_df = stratified_sample(
|
|
df_transactions,
|
|
label_column="transaction_type",
|
|
sample_fractions=sample_fractions
|
|
)
|
|
|
|
print(f"Original: {df_transactions.count()} rader")
|
|
print(f"Balansert: {balanced_df.count()} rader")
|
|
print("Klassefordeling:")
|
|
balanced_df.groupBy("transaction_type").count().show()
|
|
```
|
|
|
|
### Oversampling og undersampling-teknikker
|
|
|
|
```python
|
|
def oversample_minority_class(df, label_column, minority_class, target_ratio=0.5):
|
|
"""
|
|
Oversample minoritetsklassen ved a duplisere rader.
|
|
|
|
Args:
|
|
target_ratio: Onsket andel av minoritetsklassen
|
|
"""
|
|
class_counts = df.groupBy(label_column).count().collect()
|
|
counts = {row[label_column]: row["count"] for row in class_counts}
|
|
|
|
minority_count = counts[minority_class]
|
|
majority_count = sum(v for k, v in counts.items() if k != minority_class)
|
|
|
|
# Beregn hvor mange ganger minoritetsklassen ma dupliseres
|
|
desired_minority = int(majority_count * target_ratio / (1 - target_ratio))
|
|
oversample_factor = desired_minority / minority_count
|
|
|
|
# Oversample
|
|
minority_df = df.filter(F.col(label_column) == minority_class)
|
|
majority_df = df.filter(F.col(label_column) != minority_class)
|
|
|
|
oversampled = minority_df.sample(
|
|
withReplacement=True,
|
|
fraction=oversample_factor,
|
|
seed=42
|
|
)
|
|
|
|
return majority_df.unionByName(oversampled)
|
|
|
|
# Bruk
|
|
balanced = oversample_minority_class(
|
|
df_training,
|
|
label_column="incident_type",
|
|
minority_class="accident",
|
|
target_ratio=0.3 # 30% ulykker i treningsdatasettet
|
|
)
|
|
```
|
|
|
|
### SMOTE-lignende syntetisk oversampling
|
|
|
|
```python
|
|
from pyspark.ml.feature import VectorAssembler
|
|
from pyspark.ml.clustering import KMeans
|
|
import numpy as np
|
|
|
|
def synthetic_oversampling(df, feature_columns, label_column, minority_class, n_synthetic):
|
|
"""
|
|
Generer syntetiske minoritetseksempler basert pa naeromrade-interpolering.
|
|
"""
|
|
minority_df = df.filter(F.col(label_column) == minority_class)
|
|
|
|
# Vektoriser features
|
|
assembler = VectorAssembler(inputCols=feature_columns, outputCol="features")
|
|
vectorized = assembler.transform(minority_df)
|
|
|
|
# For hver minoritetsrad: finn naermeste nabo og interpolder
|
|
# Forenklet implementering med KMeans for cluster-sentre
|
|
kmeans = KMeans(k=min(n_synthetic, minority_df.count()), seed=42)
|
|
model = kmeans.fit(vectorized)
|
|
|
|
# Bruk cluster-sentrene som syntetiske punkter
|
|
centers = model.clusterCenters()
|
|
|
|
synthetic_rows = []
|
|
for center in centers:
|
|
row = {col: float(center[i]) for i, col in enumerate(feature_columns)}
|
|
row[label_column] = minority_class
|
|
row["_synthetic"] = True
|
|
synthetic_rows.append(row)
|
|
|
|
synthetic_df = spark.createDataFrame(synthetic_rows)
|
|
return df.withColumn("_synthetic", F.lit(False)).unionByName(synthetic_df, allowMissingColumns=True)
|
|
```
|
|
|
|
---
|
|
|
|
## Active Learning and Uncertainty Sampling
|
|
|
|
### Prinsippet bak aktiv laering
|
|
|
|
Aktiv laering velger de mest informative eksemplene for merking, i stedet for a merke tilfeldig:
|
|
|
|
```
|
|
+-- Umerkede data (pool) --+
|
|
| |
|
|
| [Hogt sikker] -> Hopp over, allerede laert
|
|
| [Moderat sikker] -> Hopp over
|
|
| [Usikker] -> MERK DENNE! <-- Mest laererik
|
|
| [Veldig usikker] -> MERK DENNE! <-- Hoyest prioritet
|
|
| |
|
|
+---------------------------+
|
|
```
|
|
|
|
### Usikkerhetssamplings-strategier
|
|
|
|
| Strategi | Formel | Best for |
|
|
|----------|--------|----------|
|
|
| **Least Confidence** | 1 - max(P(y\|x)) | Generell klassifisering |
|
|
| **Margin Sampling** | P(y1\|x) - P(y2\|x) | Naere beslutningsgrenser |
|
|
| **Entropy Sampling** | -sum(P(y\|x) * log P(y\|x)) | Multi-class problemer |
|
|
| **Query-by-Committee** | Uenighet mellom modeller | Ensemble-basert |
|
|
|
|
### Implementering av aktiv laering
|
|
|
|
```python
|
|
import numpy as np
|
|
from sklearn.ensemble import RandomForestClassifier
|
|
|
|
class ActiveLearner:
|
|
"""
|
|
Aktiv laering med usikkerhetssampling for iterativ datamerking.
|
|
"""
|
|
|
|
def __init__(self, model=None, strategy="entropy"):
|
|
self.model = model or RandomForestClassifier(n_estimators=100)
|
|
self.strategy = strategy
|
|
self.labeled_indices = []
|
|
self.labels = []
|
|
|
|
def initial_sample(self, X, n_initial=100):
|
|
"""Velg et tilfeldig initialt treningssett."""
|
|
indices = np.random.choice(len(X), n_initial, replace=False)
|
|
self.labeled_indices = list(indices)
|
|
return indices
|
|
|
|
def query(self, X, n_samples=50):
|
|
"""Velg de mest informative eksemplene for merking."""
|
|
# Prediksjonssannsynligheter for umerkede data
|
|
unlabeled_mask = np.ones(len(X), dtype=bool)
|
|
unlabeled_mask[self.labeled_indices] = False
|
|
unlabeled_indices = np.where(unlabeled_mask)[0]
|
|
|
|
if len(unlabeled_indices) == 0:
|
|
return np.array([])
|
|
|
|
X_unlabeled = X[unlabeled_indices]
|
|
probas = self.model.predict_proba(X_unlabeled)
|
|
|
|
# Beregn usikkerhetsscorer
|
|
if self.strategy == "entropy":
|
|
scores = -np.sum(probas * np.log(probas + 1e-10), axis=1)
|
|
elif self.strategy == "least_confidence":
|
|
scores = 1 - np.max(probas, axis=1)
|
|
elif self.strategy == "margin":
|
|
sorted_probas = np.sort(probas, axis=1)
|
|
scores = 1 - (sorted_probas[:, -1] - sorted_probas[:, -2])
|
|
|
|
# Velg top-n mest usikre
|
|
top_indices = np.argsort(scores)[-n_samples:]
|
|
return unlabeled_indices[top_indices]
|
|
|
|
def teach(self, X, y, indices, labels):
|
|
"""Oppdater modellen med nylig merkede data."""
|
|
self.labeled_indices.extend(indices)
|
|
self.labels.extend(labels)
|
|
|
|
X_labeled = X[self.labeled_indices]
|
|
y_labeled = np.array(self.labels)
|
|
|
|
self.model.fit(X_labeled, y_labeled)
|
|
|
|
# Brukseksempel
|
|
learner = ActiveLearner(strategy="entropy")
|
|
|
|
# Runde 1: Tilfeldig initialt sett
|
|
initial_idx = learner.initial_sample(X_pool, n_initial=100)
|
|
initial_labels = get_labels_from_labelers(X_pool[initial_idx])
|
|
learner.teach(X_pool, None, initial_idx, initial_labels)
|
|
|
|
# Runde 2-N: Aktiv laering
|
|
for round_num in range(10):
|
|
query_idx = learner.query(X_pool, n_samples=50)
|
|
new_labels = get_labels_from_labelers(X_pool[query_idx])
|
|
learner.teach(X_pool, None, query_idx, new_labels)
|
|
print(f"Runde {round_num + 1}: Totalt merket = {len(learner.labeled_indices)}")
|
|
```
|
|
|
|
---
|
|
|
|
## Crowdsourcing and Labeling Platforms
|
|
|
|
### Azure Machine Learning Data Labeling
|
|
|
|
Azure ML tilbyr en komplett merkeplattform med stotte for:
|
|
|
|
| Funksjon | Beskrivelse |
|
|
|----------|-------------|
|
|
| **Bildeklassifisering** | Multi-class og multi-label |
|
|
| **Objektdeteksjon** | Bounding boxes |
|
|
| **Instanssegmentering** | Polygoner |
|
|
| **Semantisk segmentering** | Piksel-niva (preview) |
|
|
| **Tekstklassifisering** | Single og multi-label |
|
|
| **Named Entity Recognition** | Tekst-span-merking |
|
|
|
|
### Opprette et merkeprosjekt
|
|
|
|
```python
|
|
# Azure ML SDK v2 - Opprett bildeklassifiseringsprosjekt
|
|
from azure.ai.ml import MLClient
|
|
from azure.ai.ml.entities import DataLabelingJob
|
|
|
|
# Opprett klient
|
|
ml_client = MLClient(credential, subscription_id, resource_group, workspace_name)
|
|
|
|
# Definer merkeprosjekt
|
|
labeling_job = DataLabelingJob(
|
|
display_name="traffic-sign-classification",
|
|
description="Klassifiser trafikkskilt fra vegkamera",
|
|
labeling_job_type="ImageClassificationMulticlass",
|
|
data={"uri": "azureml://datastores/images/paths/traffic_signs/"},
|
|
labels={
|
|
"classes": [
|
|
{"name": "speed_limit", "display_name": "Fartsgrense"},
|
|
{"name": "stop", "display_name": "Stopp"},
|
|
{"name": "yield", "display_name": "Vikeplikt"},
|
|
{"name": "no_entry", "display_name": "Innkjoring forbudt"},
|
|
{"name": "pedestrian", "display_name": "Fotgjenger"},
|
|
{"name": "construction", "display_name": "Veiarbeid"},
|
|
{"name": "other", "display_name": "Annet"}
|
|
]
|
|
},
|
|
properties={
|
|
"ml_assist_enabled": True, # Aktiver ML-assistert merking
|
|
"consensus_labeling_enabled": True, # Krev konsensus
|
|
"min_label_count": 2 # Minimum 2 merkere per bilde
|
|
}
|
|
)
|
|
|
|
# Opprett prosjekt
|
|
created_job = ml_client.labeling_jobs.begin_create_or_update(labeling_job)
|
|
```
|
|
|
|
### ML-Assisted Labeling
|
|
|
|
ML-assistert merking i Azure ML akselererer prosessen gjennom to faser:
|
|
|
|
```
|
|
Fase 1: CLUSTERING
|
|
+-- Merkere merker ~300 bilder manuelt
|
|
+-- ML-modell grupperer lignende bilder
|
|
+-- Merkere ser klynger av like bilder (raskere merking)
|
|
|
|
Fase 2: PRE-LABELING
|
|
+-- Modell trenes pa merkede data
|
|
+-- Modell foreslaar etiketter for umerkede bilder
|
|
+-- Merkere bekrefter/korrigerer forhondsetiketter
|
|
+-- Prosessen gjentas iterativt
|
|
```
|
|
|
|
**Viktige hensyn:**
|
|
- Transfer learning akselererer opplaering: Noen ganger trengs kun 300 merkede eksempler
|
|
- Konsensus-etiketter brukes for trening naar aktivert
|
|
- Bilder shuffles tilfeldig for a redusere bias
|
|
- Tekstinnholdet begrenses til ~128 ord for treningseffektivitet
|
|
|
|
---
|
|
|
|
## Quality Control and Inter-Rater Agreement
|
|
|
|
### Konsensus-merking
|
|
|
|
```python
|
|
# Implementer konsensusbasert kvalitetskontroll
|
|
from collections import Counter
|
|
|
|
def calculate_inter_rater_agreement(labels_per_item: dict) -> dict:
|
|
"""
|
|
Beregn inter-rater agreement (IRA) for merkede data.
|
|
|
|
Args:
|
|
labels_per_item: {item_id: [label_from_rater1, label_from_rater2, ...]}
|
|
|
|
Returns:
|
|
Statistikk over enighet
|
|
"""
|
|
agreements = []
|
|
disagreements = []
|
|
|
|
for item_id, labels in labels_per_item.items():
|
|
counter = Counter(labels)
|
|
most_common_label, most_common_count = counter.most_common(1)[0]
|
|
total_raters = len(labels)
|
|
|
|
agreement_ratio = most_common_count / total_raters
|
|
|
|
if agreement_ratio >= 0.8:
|
|
agreements.append({
|
|
"item_id": item_id,
|
|
"consensus_label": most_common_label,
|
|
"agreement": agreement_ratio
|
|
})
|
|
else:
|
|
disagreements.append({
|
|
"item_id": item_id,
|
|
"labels": dict(counter),
|
|
"agreement": agreement_ratio
|
|
})
|
|
|
|
total = len(labels_per_item)
|
|
return {
|
|
"total_items": total,
|
|
"agreed": len(agreements),
|
|
"disagreed": len(disagreements),
|
|
"agreement_rate": round(len(agreements) / max(total, 1) * 100, 1),
|
|
"disagreed_items": disagreements[:10] # Vis topp 10 uenigheter
|
|
}
|
|
```
|
|
|
|
### Cohens Kappa for kvalitetsmalinger
|
|
|
|
```python
|
|
from sklearn.metrics import cohen_kappa_score
|
|
|
|
def evaluate_labeler_quality(rater1_labels, rater2_labels):
|
|
"""
|
|
Beregn Cohens Kappa mellom to merkere.
|
|
|
|
Tolkning:
|
|
- < 0.20: Darlig enighet
|
|
- 0.21-0.40: Moderat enighet
|
|
- 0.41-0.60: Moderat enighet
|
|
- 0.61-0.80: Substansiell enighet
|
|
- 0.81-1.00: Naer perfekt enighet
|
|
"""
|
|
kappa = cohen_kappa_score(rater1_labels, rater2_labels)
|
|
|
|
if kappa < 0.40:
|
|
quality = "LAV - Gjennomga retningslinjer og gi oppfolging"
|
|
elif kappa < 0.60:
|
|
quality = "MODERAT - Akseptabel for screening, ikke for endelig trening"
|
|
elif kappa < 0.80:
|
|
quality = "GOD - Akseptabel for de fleste ML-oppgaver"
|
|
else:
|
|
quality = "UTMERKET - Hoy kvalitet for treningsdata"
|
|
|
|
return {"kappa": round(kappa, 3), "quality": quality}
|
|
|
|
# Eksempel
|
|
result = evaluate_labeler_quality(
|
|
rater1_labels=["positive", "negative", "positive", "neutral", "positive"],
|
|
rater2_labels=["positive", "negative", "neutral", "neutral", "positive"]
|
|
)
|
|
```
|
|
|
|
### Kvalitetskontrollpipeline
|
|
|
|
```
|
|
1. Initial merking (2-3 merkere per element)
|
|
|
|
|
2. Beregn inter-rater agreement (IRA)
|
|
|
|
|
3. IRA >= 80%? --> Bruk konsensus-etikett
|
|
|
|
|
4. IRA < 80%? --> Send til ekspert-merker (adjudicator)
|
|
|
|
|
5. Ekspert avgjer endelig etikett
|
|
|
|
|
6. Oppdater merkeretningslinjer basert pa uenigheter
|
|
|
|
|
7. Re-tren ML-assist-modell med nye etiketter
|
|
```
|
|
|
|
---
|
|
|
|
## Feedback Loops for Continuous Labeling
|
|
|
|
### Produksjonsdata tilbake til merking
|
|
|
|
```python
|
|
def identify_candidates_for_relabeling(model, new_data_df, confidence_threshold=0.6):
|
|
"""
|
|
Identifiser prediksjoner med lav konfidens for manuell gjennomgang.
|
|
"""
|
|
predictions = model.predict_proba(new_data_df)
|
|
|
|
low_confidence = new_data_df.filter(
|
|
F.col("prediction_confidence") < confidence_threshold
|
|
)
|
|
|
|
# Prioriter etter usikkerhet
|
|
candidates = low_confidence.orderBy(F.col("prediction_confidence").asc())
|
|
|
|
# Legg til i merke-ko
|
|
candidates.select(
|
|
"record_id", "features", "prediction", "prediction_confidence"
|
|
).write.format("delta").mode("append") \
|
|
.saveAsTable("lakehouse.default.labeling_queue")
|
|
|
|
return candidates.count()
|
|
|
|
# Kjor daglig
|
|
n_candidates = identify_candidates_for_relabeling(
|
|
model=production_model,
|
|
new_data_df=todays_predictions,
|
|
confidence_threshold=0.6
|
|
)
|
|
print(f"{n_candidates} nye elementer lagt til merke-koen")
|
|
```
|
|
|
|
### Drift-deteksjon og re-merking
|
|
|
|
```python
|
|
def detect_label_drift(historical_labels_df, recent_labels_df, columns):
|
|
"""
|
|
Oppdager endringer i etikettdistribusjon over tid.
|
|
"""
|
|
from scipy.stats import chi2_contingency
|
|
|
|
for col in columns:
|
|
hist_dist = historical_labels_df.groupBy(col).count().toPandas()
|
|
recent_dist = recent_labels_df.groupBy(col).count().toPandas()
|
|
|
|
# Chi-kvadrat-test
|
|
contingency = hist_dist.merge(recent_dist, on=col, suffixes=["_hist", "_recent"])
|
|
chi2, p_value, dof, expected = chi2_contingency(
|
|
contingency[["count_hist", "count_recent"]].values.T
|
|
)
|
|
|
|
if p_value < 0.05:
|
|
print(f"DRIFT OPPDAGET i '{col}': p={p_value:.4f}")
|
|
print(f" Historisk: {dict(zip(hist_dist[col], hist_dist['count']))}")
|
|
print(f" Nylig: {dict(zip(recent_dist[col], recent_dist['count']))}")
|
|
```
|
|
|
|
---
|
|
|
|
## Referanser
|
|
|
|
- [Set up an image labeling project](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-create-image-labeling-projects) -- Bildedatamerking i Azure ML
|
|
- [Set up a text labeling project](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-create-text-labeling-projects) -- Tekstdatamerking i Azure ML
|
|
- [Labeling images and text documents](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-label-data) -- Merkerverktoy og ML-assistert merking
|
|
- [Prepare data for computer vision tasks](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-prepare-datasets-for-automl-images) -- Data for AutoML-bildemodeller
|
|
- [Label text data for training](https://learn.microsoft.com/en-us/azure/ai-services/language-service/custom-text-classification/how-to/tag-data) -- Merking for Custom Language Models
|
|
- [Create and explore datasets with labels](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-use-labeled-dataset) -- Bruk av merkede datasett i Azure ML
|
|
|
|
---
|
|
|
|
## For Cosmo
|
|
|
|
- **Bruk denne referansen** naar kunder planlegger datamerkings-prosjekter for ML-modeller, eller naar de trenger strategier for a hondtere ubalanserte datasett.
|
|
- **Azure ML Data Labeling er forstevalget** for merkingsprosjekter i Microsoft-stakken. ML-assistert merking kan redusere manuelt arbeid med 50-80% etter initiell opplaering.
|
|
- **Aktiv laering bor alltid vurderes** for store umerkede datasett -- det reduserer merkekostnader dramatisk ved a prioritere de mest informative eksemplene.
|
|
- **Kvalitetskontroll er ikke valgfritt**: Krev konsensus mellom merkere (minimum 2), mal inter-rater agreement, og ha en ekspert-adjudicator for uenigheter.
|
|
- **For norsk offentlig sektor**: Vurder personvernaspekter ved merking av data som kan inneholde personopplysninger. Bruk PII-deteksjon for a fjerne sensitiv informasjon for merkerne ser dataene.
|