Skip to content

Commit

Permalink
Rewrite Detoxify using zero shot classification
Browse files Browse the repository at this point in the history
  • Loading branch information
Hartorn committed Sep 20, 2023
1 parent 2fb6732 commit 7fe3243
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 306 deletions.
35 changes: 20 additions & 15 deletions .github/workflows/build-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,17 @@ on:

pull_request: # This will allow to trigger on PR only with a specific label
types: [opened, reopened, synchronize, labeled, unlabeled]
# https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners
# Concurrency : auto-cancel "old" jobs ie when pushing again
# https://docs.github.com/fr/actions/using-jobs/using-concurrency
concurrency:
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
cancel-in-progress: true
env:
RUN_TESTS: false
BUILD_ONLY: false
REGISTRY_IMAGE: giskardai/giskard
DOCKERHUB_USER: giskardai
# https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners
jobs:
build-images:
# Debug
Expand Down Expand Up @@ -161,20 +166,20 @@ jobs:
${{ matrix.platform}}
cache-from: type=gha

- name: Run python integration test inside docker
if: ${{ env.RUN_TESTS }}
uses: docker/build-push-action@v5
with:
context: .
target: integration-test-python
push: false
load: false
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
builder: ${{ steps.builder.outputs.name }}
platforms: |
${{ matrix.platform}}
cache-from: type=gha
# - name: Run python integration test inside docker
# if: ${{ env.RUN_TESTS }}
# uses: docker/build-push-action@v5
# with:
# context: .
# target: integration-test-python
# push: false
# load: false
# tags: ${{ steps.meta.outputs.tags }}
# labels: ${{ steps.meta.outputs.labels }}
# builder: ${{ steps.builder.outputs.name }}
# platforms: |
# ${{ matrix.platform}}
# cache-from: type=gha

- name: Build and push
id: build
Expand Down
47 changes: 11 additions & 36 deletions .github/workflows/build_backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,50 +14,29 @@ on:
required: true
type: boolean
default: false
is-dispatch:
description: 'Just to identify manual dispatch'
required: true
type: boolean
default: true
workflow_call:
inputs:
run-integration-tests:
description: 'If integration test should be run'
required: true
type: boolean
default: false
# Concurrency : auto-cancel "old" jobs ie when pushing again
# https://docs.github.com/fr/actions/using-jobs/using-concurrency
concurrency:
group: ${{ github.workflow }}-${{ inputs.run-integration-tests }}-${{ inputs.is-dispatch }}-${{ github.ref || github.run_id }}
cancel-in-progress: true
env:
GSK_DISABLE_ANALYTICS: true
defaults:
run:
shell: bash
jobs:
pre-check:
name: Pre check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 1
# Inspired from https://blog.pantsbuild.org/skipping-github-actions-jobs-without-breaking-branch-protection/
- id: files
name: Get changed files outside python-client
uses: tj-actions/changed-files@v39
with:
files_ignore: python-client/**

- id: files-python
name: Get changed files in python-client
uses: tj-actions/changed-files@v39
with:
files: python-client/**

- id: python_only
if: steps.files.outputs.any_changed != 'true'
name: Check for changes in python only
run: echo 'python_only=PYTHON_ONLY' >> $GITHUB_OUTPUT

- id: python_at_least
if: steps.files-python.outputs.any_changed != 'true'
name: Check for changes in python
run: echo 'python_at_least=PYTHON_AT_LEAST' >> $GITHUB_OUTPUT

sonar:
if: ${{ github.actor != 'dependabot[bot]' && (github.event_name == 'pull_request' || github.event_name == 'push') }}
name: Sonar
Expand Down Expand Up @@ -93,8 +72,6 @@ jobs:
run: ./gradlew sonar --info --parallel

build:
needs: pre-check
if: ${{ needs.pre-check.outputs.python_only != 'PYTHON_ONLY' }}
name: Backend
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -147,8 +124,6 @@ jobs:

build-python:
name: Build Python
needs: pre-check
if: ${{ needs.pre-check.outputs.python_at_least != 'PYTHON_AT_LEAST' }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false # Do not stop when any job fails
Expand Down Expand Up @@ -228,4 +203,4 @@ jobs:
working-directory: python-client
env:
PYTEST_XDIST_AUTO_NUM_WORKERS: ${{ matrix.os == 'windows-2019' && 1 || 2 }}
run: pdm run test
run: pdm run test -m "slow"
29 changes: 29 additions & 0 deletions python-client/giskard/scanner/llm/detoxify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import pandas as pd
from transformers import pipeline


# Re-implementation based on https://github.com/unitaryai/detoxify/issues/15#issuecomment-900443551
class Detoxify:
def __init__(
self,
):
super().__init__()
self.pipeline = pipeline(
"text-classification",
model="unitary/unbiased-toxic-roberta",
tokenizer="unitary/unbiased-toxic-roberta",
function_to_apply="sigmoid",
return_all_scores=True,
)

def predict(self, text) -> pd.DataFrame:
inputs = [text] if isinstance(text, str) else text
results = self.pipeline(inputs)
output = []
for one_result in results:
res = {}
for single_label_result in one_result:
res[single_label_result["label"]] = single_label_result["score"]
output.append(res)
output = pd.DataFrame(output)
return output
9 changes: 3 additions & 6 deletions python-client/giskard/scanner/llm/harmfulness_detector.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from pathlib import Path
from typing import List, Sequence

import numpy as np
import pandas as pd

from ...datasets import Dataset
Expand Down Expand Up @@ -73,13 +72,11 @@ def run(self, model: LangchainModel, dataset: Dataset) -> Sequence[Issue]:

def _compute_harmfulness(self, sentences: List[str]):
try:
from detoxify import Detoxify
from giskard.scanner.llm.detoxify import Detoxify
except ImportError as err:
raise LLMImportError() from err

keys = ["toxicity", "severe_toxicity", "identity_attack", "insult", "threat"]
results = Detoxify("unbiased").predict(list(sentences))

harmfulness = np.vstack([results[k] for k in keys]).max(axis=0)

results = Detoxify().predict(list(sentences))
harmfulness = results[keys].max(axis="columns")
return harmfulness
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,9 @@ def run(self, model: LangchainModel, dataset: Dataset) -> Sequence[Issue]:

def _compute_bias(self, sentences: List[str]):
try:
from detoxify import Detoxify
from giskard.scanner.llm.detoxify import Detoxify
except ImportError as err:
raise LLMImportError() from err

results = Detoxify("unbiased").predict(list(sentences))

return results["identity_attack"]
results = Detoxify().predict(list(sentences))
return results["identity_attack"]
6 changes: 3 additions & 3 deletions python-client/giskard/scanner/llm/toxicity_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,11 @@ def run(self, model: LangchainModel, dataset: Dataset) -> Sequence[Issue]:

def _compute_toxicity_score(self, sentences: List[str]):
try:
from detoxify import Detoxify
from giskard.scanner.llm.detoxify import Detoxify
except ImportError as err:
raise LLMImportError() from err

return Detoxify("unbiased").predict(list(sentences))["toxicity"]
results = Detoxify().predict(list(sentences))
return results["toxicity"]


@dataclass
Expand Down
Loading

0 comments on commit 7fe3243

Please sign in to comment.