Ajuste fino com privacidade diferencial usando o Opacus
Ajuste um classificador de texto ou de imagens nos rótulos do seu estudo com DP-SGD, defina epsilon e delta e leia a privacidade gasta no log da execução e no cartão do modelo.
Novo na v2.14.0
Este tutorial treina um classificador nos rótulos do próprio estudo com privacidade diferencial, de modo que o modelo divulgado revele pouco sobre qualquer item anotado. A página é para responsáveis por estudos cujos itens ou rótulos são sensíveis (por exemplo prontuários de saúde ou mensagens privadas) e que pretendem compartilhar ou publicar o modelo. Você vai adicionar um bloco dp aos parâmetros de hf-text-classification, executar o treinador no exemplo de avaliações de produtos e ler a privacidade gasta no log da execução e no cartão do modelo. O mesmo bloco funciona em hf-image-classification, e nenhum outro treinador aceita dp.
Epsilon e delta na privacidade diferencial
A privacidade diferencial é uma garantia matemática sobre uma computação em um conjunto de dados. Adicionar ou remover um registro altera a probabilidade de qualquer saída em uma quantidade limitada, então a saída revela pouco sobre se esse registro estava presente. O epsilon (ε) é o limite. Um epsilon menor torna mais difícil distinguir as saídas com e sem o registro, e um epsilon menor portanto dá privacidade mais forte. O delta (δ) é a probabilidade de que o limite do epsilon falhe, e a documentação do Potato descreve o delta como "a chance de a garantia não valer".
O DP-SGD treina uma rede neural sob essa garantia, pelo método de Abadi et al. (2016). Cada passo de treinamento corta o gradiente de cada item a uma norma máxima e adiciona ruído gaussiano à soma. O corte limita o quanto um item pode mover o modelo, e o ruído mascara esse movimento limitado. O Opacus é uma biblioteca PyTorch que implementa o DP-SGD, e o Potato entrega o laço de treinamento ao Opacus sempre que uma execução carrega dp. O contador RDP do Opacus soma o epsilon que cada passo gasta.
Instale o treinador e o Opacus
Os treinadores de transformers precisam de torch e transformers dos extras de treinamento do Potato. Nenhum extra do Potato inclui o Opacus, porque o Opacus fixa versões específicas do torch. Instale os dois na máquina em que a execução treina:
pip install "potato-annotation[train-text]" # [train-vision] for hf-image-classification
pip install opacusUma execução com dp verifica o Opacus na máquina de treinamento antes de o treinamento começar. Em um backend ssh, SLURM ou de agente, o Opacus portanto vai no servidor com GPU ou no nó de computação que treina, e não no servidor de anotação. O treinamento remoto cobre esses backends.
Configure o treinador
O config abaixo é o exemplo de avaliações de produtos de Treine o seu primeiro modelo, com o treinamento privado definido como padrão para hf-text-classification. Copie data/reviews.json e data/gold.json de examples/ai-assisted/model-training/ do Potato para uma pasta de projeto e salve este config nela como config.yaml:
annotation_task_name: "Review Sentiment (private training)"
data_files:
- "data/reviews.json"
item_properties:
id_key: "id"
text_key: "text"
task_dir: "."
output_annotation_dir: "annotation_output"
require_password: false
admin_api_key: "demo-admin-key"
num_annotators_per_item: 1
annotation_schemes:
- annotation_type: "radio"
name: "sentiment"
description: "Is this review positive or negative?"
labels:
- "positive"
- "negative"
sequential_key_binding: true
model_training:
enabled: true
trainer_params:
hf-text-classification:
epochs: 5
batch_size: 16
dp:
epsilon: 8
max_grad_norm: 1.0
writeback:
schemas: ["sentiment"]
include_unlabeled: true
min_confidence: 0.6trainer_params define os padrões de toda execução de hf-text-classification, então o campo Parameters da página Training abre com a entrada dp já preenchida. epsilon: 8 fica dentro do intervalo de 1 a 10 que a documentação do Potato chama de usual. delta foi deixado de fora, então o Potato define o delta como 1 / (10 × itens train). max_grad_norm: 1.0 repete o corte padrão. epochs e batch_size repetem os padrões do treinador, porque o Opacus dimensiona o ruído a partir dos dois. require_password: false e a chave de administrador simples servem apenas para um teste local.
dp aceita três chaves:
| Chave | Padrão | Significado |
|---|---|---|
epsilon | obrigatório | O orçamento de privacidade para a execução inteira. Um epsilon menor é mais privado e adiciona mais ruído. |
delta | 1 / (10 × itens train) | A chance de a garantia não valer. O delta deve ser menor que 1 / itens train. |
max_grad_norm | 1.0 | O gradiente de cada item é cortado a essa norma antes de o ruído ser adicionado. |
O config passa em python -m potato.validate_cli config.yaml --strict. O validador aceita um mapeamento, null ou false para dp e recusa dp: true. O validador também verifica cada chave dentro de dp e o intervalo da chave. O Opacus e as verificações que dependem do número de itens train executam quando uma execução começa, na máquina que treina, como lista Erros no início de uma execução.
Rotule itens e inicie uma execução
hf-text-classification se recusa a ajustar com menos de 20 itens train (min_instances). Inicie o servidor e depois rotule as avaliações à mão ou com três anotadores simulados do simulador de usuários, que respondem a partir do arquivo gold:
potato start config.yaml -p 8000
python -m potato.simulator --server http://localhost:8000 --users 3 \
--strategy gold_standard --competence good=1.0 --max-annotations 40 \
--gold-file data/gold.json --sequential --fast-mode --no-exportO simulador rotula 120 das 160 avaliações. Com o splits padrão (70% train, 15% val, 15% test) e split_seed: 0, a nossa execução pôs 76 avaliações rotuladas em train, 19 em val e 25 em test. O delta portanto assume por padrão 1 / 760, cerca de 0,00132, e qualquer delta que você definir deve ser menor que 1 / 76, cerca de 0,0132.
Abra http://localhost:8000/admin/training com a chave de administrador, escolha hf-text-classification e o esquema sentiment, confira se o campo Parameters mostra a entrada dp e clique em Train now. A mesma execução começa pela API:
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
http://localhost:8000/admin/training/api/runs \
-d '{"trainer": "hf-text-classification", "schemas": ["sentiment"]}'A resposta traz o run_id. Os params da própria execução substituem trainer_params, então "params": {"dp": {"epsilon": 2}} treina uma execução com um orçamento menor, e "params": {"dp": null} ou "params": {"dp": false} treina uma execução sem DP. Mantenha split_seed inalterado entre as duas execuções, para que ambas treinem e pontuem nos mesmos itens e as pontuações em itens reservados sejam diretamente comparáveis.
Linhas do log da execução sob DP
A página da execução em /admin/training/runs/<run_id> lista os eventos e as métricas da execução. Uma execução com DP adiciona três tipos de linha aos eventos.
A primeira linha nomeia as tabelas de embeddings que o Potato congela. Os embeddings de posição e de tipo de token de um transformer de texto usam uma linha compartilhada por todo o lote, e o Opacus não consegue calcular um gradiente por item através de uma linha compartilhada. Para o modelo padrão, distilbert/distilroberta-base, as tabelas congeladas são roberta.embeddings.position_embeddings e roberta.embeddings.token_type_embeddings. As tabelas congeladas não treinam e não gastam privacidade, e os embeddings de palavras e todas as outras camadas continuam treinando.
A segunda linha começa com DP-SGD: e informa o epsilon alvo, o delta, as épocas planejadas, o multiplicador de ruído que o Opacus escolheu, o corte por item e a taxa de amostragem. Cada passo sorteia um lote por amostragem de Poisson, então cada item train entra em um lote de forma independente com probabilidade 1 / lotes por época. Com 76 itens train em lotes de 16, uma época tem 5 lotes e o log dá uma taxa de amostragem de 0.2000. O multiplicador de ruído define o desvio-padrão do ruído, e o FAQ do Opacus dá 0,1 a 2 como o intervalo usual.
Após cada época, uma linha da forma Epoch <n>/<epochs>: loss <loss>, epsilon spent <epsilon> informa a perda média da época e o epsilon gasto até então. O mesmo valor é registrado por época como a métrica dp_epsilon, e as métricas finais da execução carregam dp_epsilon para a execução inteira. O Opacus dimensiona o ruído para que as épocas planejadas gastem no máximo o epsilon alvo. Uma execução que para cedo por patience (duas épocas sem uma pontuação val melhor, por padrão) gasta menos que o alvo.
A entrada dp no cartão do modelo
A execução grava model_card.json em artifacts/ no diretório da execução, <task_dir>/<output_annotation_dir>/training/runs/<run_id>/. O cartão de uma execução com DP tem uma entrada dp:
| Campo | Valor |
|---|---|
epsilon_target | O epsilon que você pediu |
epsilon_spent | O que a execução gastou, pelo contador RDP do Opacus |
delta | O delta que a execução usou, o seu próprio ou o padrão |
max_grad_norm | O corte por item |
noise_multiplier | O multiplicador de ruído que o Opacus escolheu |
accountant | rdp |
covers | train split |
epsilon_spent conta toda época que a execução treinou. As épocas depois do checkpoint mantido contam para o total. Publicar no Hugging Face Hub envia model_card.json com o modelo, com os caminhos locais substituídos por nomes de arquivo, então a entrada dp viaja com os pesos. O cartão de modelo README.md no Hub também tem uma linha "Differential privacy" com o epsilon gasto em relação ao alvo, o delta e a divisão train que a garantia cobre. A linha omite o alvo ou o delta quando a execução não registrou o valor.
Escolhendo o orçamento de privacidade e o corte
A documentação do Potato dá 1 a 10 como o intervalo usual para o epsilon e exige um delta menor que 1 / itens train. O FAQ do Opacus dá a mesma regra prática para o delta e recomenda uma constante pequena para o epsilon com um delta minúsculo. O delta padrão de 1 / (10 × itens train) fica abaixo do teto de 1 / itens train, então a maioria dos estudos pode deixar o delta de fora.
O corte, max_grad_norm, troca dois erros. O corte altera todo gradiente maior que o limiar, e o FAQ do Opacus diz que o corte prejudica a convergência. O ruído é escalado pelo corte, então um corte maior adiciona mais ruído. O FAQ acrescenta que um limiar errado prejudica mais os modelos lineares que os profundos. Comece do padrão de 1,0 e compare as pontuações em itens reservados antes de ajustar o corte.
O Opacus dimensiona o ruído para as epochs planejadas, então o mesmo epsilon distribuído por menos épocas significa menos ruído em cada passo. O FAQ do Opacus observa que um lote maior eleva a taxa de amostragem e, com ela, o custo de privacidade. O FAQ sugere uma taxa de aprendizado maior e uma parada mais cedo para compensar o custo. Compare a pontuação em itens reservados de uma execução com DP com a pontuação de uma execução sem dp na mesma divisão. A diferença entre as duas pontuações mede o que a garantia custa nos seus dados.
Escopo da garantia
A garantia cobre a divisão train. Alterar um item train altera o que o modelo divulgado poderia ser em no máximo o epsilon declarado. O item conta junto com o rótulo resolvido a partir dos anotadores do item. O Potato declara a garantia por item, e a documentação não declara nenhuma garantia por anotador.
A divisão val está fora da garantia. Após cada época, o treinador pontua o modelo nos itens val e mantém o checkpoint da época de melhor pontuação. Essa escolha lê os rótulos dos itens val sem ruído adicionado, então o epsilon no cartão do modelo não faz nenhuma promessa sobre os itens val. Quando os itens val precisam da mesma proteção que os itens train, defina a fração val como 0 e mova a fração para test, por exemplo splits: {train: 0.7, val: 0, test: 0.3}. Sem itens val, o treinador mantém a última época, patience nunca interrompe a execução mais cedo e a pontuação em itens reservados vem de test. Manter a fração train inalterada mantém os mesmos itens em train, porque o Potato atribui as divisões por um hash com semente do id de cada item.
A lista de rótulos do modelo está fora da garantia. O Potato lê a lista de rótulos a partir dos rótulos que aparecem nos dados, então um rótulo que os anotadores usaram em um único item ainda aparece entre as saídas do modelo.
Diferenças de treinamento sob DP
O DP-SGD precisa de um gradiente para cada item, então uma execução com DP difere de uma execução comum do mesmo treinador nestes pontos:
- O treinamento executa em precisão total, qualquer que seja o valor de
precision, porque a precisão mista fica desligada sob DP. gradient_accumulationdeve ser 1. Cada passo treina um lote amostrado por Poisson.- Um modelo base quantizado (
lora.quantize) é recusado. - As tabelas de embeddings de posição e de tipo de token de um transformer de texto ficam congeladas, como o log da execução informa.
- Um modelo com uma camada que o Opacus não suporta, como BatchNorm, falha no início do treinamento com a camada nomeada.
Apenas hf-text-classification e hf-image-classification aceitam dp. Todo outro treinador recusa um parâmetro que não aceita, então dp em sklearn-text, setfit ou hf-token-classification falha na inicialização em trainer_params e com um 409 quando uma execução começa.
Erros no início de uma execução
potato validate informa uma chave que dp não aceita e um valor fora do intervalo antes de o servidor iniciar. Uma execução verifica o bloco dp de novo quando começa, junto com o Opacus e os limites que dependem da divisão train. Uma execução que falha nessas verificações termina em erro antes do treinamento, com o código validation_failed e todos os problemas em uma mensagem. As mensagens vêm das verificações do Potato:
| Problema | Mensagem |
|---|---|
| Opacus não instalado | dp needs opacus, which is not installed (pip install opacus). |
Sem epsilon | dp needs epsilon, the privacy budget for the run (1 to 10 is usual). |
Uma chave que dp não aceita | dp does not take bogus; it takes epsilon, delta and max_grad_norm. |
delta igual ou acima de 1 / itens train | dp.delta is 0.05, at least 1 / 76 train items: at that delta one item's data could be released outright. Use less than 0.0131579. |
gradient_accumulation acima de 1 | dp trains one sampled batch per step; set gradient_accumulation to 1. |
lora.quantize definido | dp cannot train a quantized (4bit or 8bit) base model; drop lora.quantize. |
Um mapeamento vazio, dp: {}, pede DP sem orçamento e falha com a mensagem de epsilon ausente. Quando trainer_params define dp, um "dp": null ou "dp": false da execução desativa o DP para essa execução. A rota de status, GET /admin/training/api/status, lista as mesmas mensagens em errors.
Leitura complementar
- Visão geral do treinamento de modelos, para a página Training e a publicação no Hub
- Treinadores, para o que
hf-text-classificationehf-image-classificationajustam - Configuração do treinamento de modelos, para divisões, pontuações em itens reservados e gravação de volta
- Treine o seu primeiro modelo, para o mesmo projeto sem GPU nem DP
- Treinamento remoto, para executar o ajuste fino com DP em um servidor com GPU
- Privacidade diferencial (
dp) na referência de treinamento de modelos do Potato
Referências
- Abadi, M., Chu, A., Goodfellow, I., McMahan, H. B., Mironov, I., Talwar, K., and Zhang, L. (2016). Deep learning with differential privacy. Proceedings of the 2016 ACM SIGSAC Conference on Computer and Communications Security, 308-318. https://doi.org/10.1145/2976749.2978318
- Opacus. FAQ. https://opacus.ai/docs/faq