Heim >Backend-Entwicklung >Python-Tutorial >Maskierung vertraulicher Daten in Eingabeaufforderungen mithilfe von Regex und spaCy
Menschen haben Datenschutzbedenken in Bezug auf beliebte LLMs wie OpenAI, Gemini, Claude usw. Wir wissen nicht wirklich, was hinter den Kulissen passiert, es sei denn, es handelt sich um ein Open-Source-Modell. Wir müssen also unsererseits vorsichtig sein.
Das Erste wäre der Umgang mit Informationen, die wir an die LLMs weitergeben. Experten empfehlen, die Eingabe vertraulicher Informationen oder persönlicher Identifikatoren zu vermeiden. Klingt einfacher, aber da die Kontextgröße von LLMs zunimmt, können wir große Texte an die Modelle übergeben. Daher kann es schwierig werden, alle Identifikatoren zu überprüfen und zu maskieren.
Also habe ich versucht, ein Python-Skript zu erstellen, das Identifikatoren und vertrauliche Informationen erkennt und maskiert. Regex ist magisch und implementiert, um verschiedene vertrauliche Informationen zu erkennen und durch Masken zu ersetzen. Außerdem wird die Spacy-Bibliothek verwendet, um allgemeine Identifikatoren wie Name, Ort usw. zu erkennen,
Hinweis: Derzeit ist dies für den indischen Kontext geeignet, es kann jedoch weiterhin eine gemeinsame Kennung erkannt werden.
Schauen wir uns also die Implementierung an (bei der Implementierung habe ich die Hilfe von LLM in Anspruch genommen)
Wenn Sie die Erklärung überspringen möchten.
Hier ist der Link zur Codebasis: aditykris/prompt-masker-Indian-context
Importieren der erforderlichen Module/Bibliotheken
import re from typing import Dict, List, Tuple import spacy nlp = spacy.load("en_core_web_sm")
Sie müssen „en_core_web_sm“ mithilfe des folgenden Snippets manuell installieren
python -m spacy download en_core_web_sm
Festlegen der gemeinsamen indischen vertraulichen Informationen.
class IndianIdentifier: '''Regex for common Indian identifiers''' PAN = r'[A-Z]{5}[0-9]{4}[A-Z]{1}' AADHAR = r'[2-9]{1}[0-9]{3}\s[0-9]{4}\s[0-9]{4}' INDIAN_PASSPORT = r'[A-PR-WYa-pr-wy][1-9]\d\s?\d{4}[1-9]' DRIVING_LICENSE = r'(([A-Z]{2}[0-9]{2})( )|([A-Z]{2}-[0-9]{2}))((19|20)[0-9][0-9])[0-9]{7}' UPI_ID = r'[\.\-a-z0-9]+@[a-z]+' INDIAN_BANK_ACCOUNT = r'\d{9,18}' IFSC_CODE = r'[A-Z]{4}0[A-Z0-9]{6}' INDIAN_PHONE_NUMBER = r'(\+91|\+91\-|0)?[789]\d{9}' EMAIL = r'[\w\.-]+@[\w\.-]+\.\w+' @classmethod def get_all_patterns(cls) -> Dict[str, str]: """Returns all regex patterns defined in the class""" return { name: pattern for name, pattern in vars(cls).items() if isinstance(pattern, str) and not name.startswith('_') }
Also habe ich die Python-Klassen und -Methoden überarbeitet und sie hier implementiert.
Ich fand den regulären Ausdruck dieser Bezeichner von DebugPointer sehr hilfreich.
Nun zur Erkennungsfunktion. Einfaches re.finditer() wurde verwendet, um verschiedene Muster zu durchlaufen, um Übereinstimmungen zu finden. Übereinstimmungen werden in einer Liste gespeichert.
def find_matches(text: str, pattern: str) -> List[Tuple[int, int, str]]: """ Find all matches of a pattern in text and return their positions and matched text """ matches = [] for match in re.finditer(pattern, text): matches.append((match.start(), match.end(), match.group())) return matches
Verwendete ein einfaches Wörterbuch zum Speichern von Ersatztexten. Habe es in eine Funktion verpackt, um den Ersetzungstext zurückzugeben.
def get_replacement_text(identifier_type: str) -> str: """ Returns appropriate replacement text based on the type of identifier """ replacements = { 'PAN': '[PAN_NUMBER]', 'AADHAR': '[AADHAR_NUMBER]', 'INDIAN_PASSPORT': '[PASSPORT_NUMBER]', 'DRIVING_LICENSE': '[DL_NUMBER]', 'UPI_ID': '[UPI_ID]', 'INDIAN_BANK_ACCOUNT': '[BANK_ACCOUNT]', 'IFSC_CODE': '[IFSC_CODE]', 'INDIAN_PHONE_NUMBER': '[PHONE_NUMBER]', 'EMAIL': '[EMAIL_ADDRESS]', 'PERSON': '[PERSON_NAME]', 'ORG': '[ORGANIZATION]', 'GPE': '[LOCATION]' } return replacements.get(identifier_type, '[MASKED]')
Ah! Der Hauptteil beginnt.
def analyze_identifiers(text: str) -> Tuple[str, Dict[str, List[str]]]: """ Function to identify and hide sensitive information. Returns: - masked_text: Text with all sensitive information masked - found_identifiers: Dictionary containing all identified sensitive information """ # Initialize variables masked_text = text found_identifiers = {} positions_to_mask = [] # First, find all regex matches for identifier_name, pattern in IndianIdentifier.get_all_patterns().items(): matches = find_matches(text, pattern) if matches: found_identifiers[identifier_name] = [match[2] for match in matches] positions_to_mask.extend( (start, end, identifier_name) for start, end, _ in matches ) # Then, process named entities using spaCy doc = nlp(text) for ent in doc.ents: if ent.label_ in ["PERSON", "ORG", "GPE"]: positions_to_mask.append((ent.start_char, ent.end_char, ent.label_)) if ent.label_ not in found_identifiers: found_identifiers[ent.label_] = [] found_identifiers[ent.label_].append(ent.text) # Sort positions by start index in reverse order to handle overlapping matches positions_to_mask.sort(key=lambda x: x[0], reverse=True) # Apply masking for start, end, identifier_type in positions_to_mask: replacement = get_replacement_text(identifier_type) masked_text = masked_text[:start] + replacement + masked_text[end:] return masked_text, found_identifiers
Diese Funktion verwendet die Eingabeaufforderung als Eingabe und gibt die maskierte Eingabeaufforderung zusammen mit identifizierten Elementen als Wörterbuch zurück.
Lassen Sie es mich einzeln erklären.
Folgende Schleife durch Regex verschiedener Bezeichner, um Übereinstimmungen in der Eingabeaufforderung zu finden. Wenn es gefunden wird, dann wird es:
1. Speichern Sie identifizierte Informationen in einem Wörterbuch mit dem Identifikatortyp als Schlüssel, um den Überblick zu behalten.
2. Notiert die Positionen und speichert sie in positions_to_mask, damit wir die Maskierung später anwenden können.
import re from typing import Dict, List, Tuple import spacy nlp = spacy.load("en_core_web_sm")
Jetzt ist spacige Zeit. Es ist eine großartige Bibliothek für NLP-Aufgaben (Natural Language Processing). Mit dem NLP-Modul können wir die Bezeichner aus Text extrahieren.
Derzeit bin ich es gewohnt, Name, Organisation und Standorte zu erkennen.
Dies funktioniert wie die obige Schleife zum Identifizieren und Speichern des Standorts.
class IndianIdentifier: '''Regex for common Indian identifiers''' PAN = r'[A-Z]{5}[0-9]{4}[A-Z]{1}' AADHAR = r'[2-9]{1}[0-9]{3}\s[0-9]{4}\s[0-9]{4}' INDIAN_PASSPORT = r'[A-PR-WYa-pr-wy][1-9]\d\s?\d{4}[1-9]' DRIVING_LICENSE = r'(([A-Z]{2}[0-9]{2})( )|([A-Z]{2}-[0-9]{2}))((19|20)[0-9][0-9])[0-9]{7}' UPI_ID = r'[\.\-a-z0-9]+@[a-z]+' INDIAN_BANK_ACCOUNT = r'\d{9,18}' IFSC_CODE = r'[A-Z]{4}0[A-Z0-9]{6}' INDIAN_PHONE_NUMBER = r'(\+91|\+91\-|0)?[789]\d{9}' EMAIL = r'[\w\.-]+@[\w\.-]+\.\w+' @classmethod def get_all_patterns(cls) -> Dict[str, str]: """Returns all regex patterns defined in the class""" return { name: pattern for name, pattern in vars(cls).items() if isinstance(pattern, str) and not name.startswith('_') }
In einigen Testfällen fiel mir auf, dass einige Masken fehlten, was hauptsächlich an Überlappungen der Bezeichner lag. Das Sortieren in umgekehrter Reihenfolge hat also bei der Lösung geholfen.
def find_matches(text: str, pattern: str) -> List[Tuple[int, int, str]]: """ Find all matches of a pattern in text and return their positions and matched text """ matches = [] for match in re.finditer(pattern, text): matches.append((match.start(), match.end(), match.group())) return matches
Dann maskieren wir schließlich Ereignisse mithilfe von Daten aus „found_identifiers“ und „positions_to_mask“.
def get_replacement_text(identifier_type: str) -> str: """ Returns appropriate replacement text based on the type of identifier """ replacements = { 'PAN': '[PAN_NUMBER]', 'AADHAR': '[AADHAR_NUMBER]', 'INDIAN_PASSPORT': '[PASSPORT_NUMBER]', 'DRIVING_LICENSE': '[DL_NUMBER]', 'UPI_ID': '[UPI_ID]', 'INDIAN_BANK_ACCOUNT': '[BANK_ACCOUNT]', 'IFSC_CODE': '[IFSC_CODE]', 'INDIAN_PHONE_NUMBER': '[PHONE_NUMBER]', 'EMAIL': '[EMAIL_ADDRESS]', 'PERSON': '[PERSON_NAME]', 'ORG': '[ORGANIZATION]', 'GPE': '[LOCATION]' } return replacements.get(identifier_type, '[MASKED]')
Eine Beispieleingabe dieses Programms wäre:
Eingabe:
def analyze_identifiers(text: str) -> Tuple[str, Dict[str, List[str]]]: """ Function to identify and hide sensitive information. Returns: - masked_text: Text with all sensitive information masked - found_identifiers: Dictionary containing all identified sensitive information """ # Initialize variables masked_text = text found_identifiers = {} positions_to_mask = [] # First, find all regex matches for identifier_name, pattern in IndianIdentifier.get_all_patterns().items(): matches = find_matches(text, pattern) if matches: found_identifiers[identifier_name] = [match[2] for match in matches] positions_to_mask.extend( (start, end, identifier_name) for start, end, _ in matches ) # Then, process named entities using spaCy doc = nlp(text) for ent in doc.ents: if ent.label_ in ["PERSON", "ORG", "GPE"]: positions_to_mask.append((ent.start_char, ent.end_char, ent.label_)) if ent.label_ not in found_identifiers: found_identifiers[ent.label_] = [] found_identifiers[ent.label_].append(ent.text) # Sort positions by start index in reverse order to handle overlapping matches positions_to_mask.sort(key=lambda x: x[0], reverse=True) # Apply masking for start, end, identifier_type in positions_to_mask: replacement = get_replacement_text(identifier_type) masked_text = masked_text[:start] + replacement + masked_text[end:] return masked_text, found_identifiers
Ausgabe:
Maskierter Text:
for identifier_name, pattern in IndianIdentifier.get_all_patterns().items(): matches = find_matches(text, pattern) if matches: found_identifiers[identifier_name] = [match[2] for match in matches] positions_to_mask.extend( (start, end, identifier_name) for start, end, _ in matches )
Das obige ist der detaillierte Inhalt vonMaskierung vertraulicher Daten in Eingabeaufforderungen mithilfe von Regex und spaCy. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!