xplia 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- venv/Lib/site-packages/_distutils_hack/__init__.py +222 -0
- venv/Lib/site-packages/_distutils_hack/override.py +1 -0
- venv/Lib/site-packages/pip/__init__.py +13 -0
- venv/Lib/site-packages/pip/__main__.py +24 -0
- venv/Lib/site-packages/pip/__pip-runner__.py +50 -0
- venv/Lib/site-packages/pip/_internal/__init__.py +19 -0
- venv/Lib/site-packages/pip/_internal/build_env.py +311 -0
- venv/Lib/site-packages/pip/_internal/cache.py +292 -0
- venv/Lib/site-packages/pip/_internal/cli/__init__.py +4 -0
- venv/Lib/site-packages/pip/_internal/cli/autocompletion.py +171 -0
- venv/Lib/site-packages/pip/_internal/cli/base_command.py +236 -0
- venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py +1074 -0
- venv/Lib/site-packages/pip/_internal/cli/command_context.py +27 -0
- venv/Lib/site-packages/pip/_internal/cli/main.py +79 -0
- venv/Lib/site-packages/pip/_internal/cli/main_parser.py +134 -0
- venv/Lib/site-packages/pip/_internal/cli/parser.py +294 -0
- venv/Lib/site-packages/pip/_internal/cli/progress_bars.py +68 -0
- venv/Lib/site-packages/pip/_internal/cli/req_command.py +508 -0
- venv/Lib/site-packages/pip/_internal/cli/spinners.py +159 -0
- venv/Lib/site-packages/pip/_internal/cli/status_codes.py +6 -0
- venv/Lib/site-packages/pip/_internal/commands/__init__.py +132 -0
- venv/Lib/site-packages/pip/_internal/commands/cache.py +222 -0
- venv/Lib/site-packages/pip/_internal/commands/check.py +54 -0
- venv/Lib/site-packages/pip/_internal/commands/completion.py +121 -0
- venv/Lib/site-packages/pip/_internal/commands/configuration.py +282 -0
- venv/Lib/site-packages/pip/_internal/commands/debug.py +199 -0
- venv/Lib/site-packages/pip/_internal/commands/download.py +147 -0
- venv/Lib/site-packages/pip/_internal/commands/freeze.py +108 -0
- venv/Lib/site-packages/pip/_internal/commands/hash.py +59 -0
- venv/Lib/site-packages/pip/_internal/commands/help.py +41 -0
- venv/Lib/site-packages/pip/_internal/commands/index.py +139 -0
- venv/Lib/site-packages/pip/_internal/commands/inspect.py +92 -0
- venv/Lib/site-packages/pip/_internal/commands/install.py +778 -0
- venv/Lib/site-packages/pip/_internal/commands/list.py +368 -0
- venv/Lib/site-packages/pip/_internal/commands/search.py +174 -0
- venv/Lib/site-packages/pip/_internal/commands/show.py +189 -0
- venv/Lib/site-packages/pip/_internal/commands/uninstall.py +113 -0
- venv/Lib/site-packages/pip/_internal/commands/wheel.py +183 -0
- venv/Lib/site-packages/pip/_internal/configuration.py +381 -0
- venv/Lib/site-packages/pip/_internal/distributions/__init__.py +21 -0
- venv/Lib/site-packages/pip/_internal/distributions/base.py +39 -0
- venv/Lib/site-packages/pip/_internal/distributions/installed.py +23 -0
- venv/Lib/site-packages/pip/_internal/distributions/sdist.py +150 -0
- venv/Lib/site-packages/pip/_internal/distributions/wheel.py +34 -0
- venv/Lib/site-packages/pip/_internal/exceptions.py +733 -0
- venv/Lib/site-packages/pip/_internal/index/__init__.py +2 -0
- venv/Lib/site-packages/pip/_internal/index/collector.py +505 -0
- venv/Lib/site-packages/pip/_internal/index/package_finder.py +1029 -0
- venv/Lib/site-packages/pip/_internal/index/sources.py +223 -0
- venv/Lib/site-packages/pip/_internal/locations/__init__.py +467 -0
- venv/Lib/site-packages/pip/_internal/locations/_distutils.py +173 -0
- venv/Lib/site-packages/pip/_internal/locations/_sysconfig.py +213 -0
- venv/Lib/site-packages/pip/_internal/locations/base.py +81 -0
- venv/Lib/site-packages/pip/_internal/main.py +12 -0
- venv/Lib/site-packages/pip/_internal/metadata/__init__.py +127 -0
- venv/Lib/site-packages/pip/_internal/metadata/_json.py +84 -0
- venv/Lib/site-packages/pip/_internal/metadata/base.py +688 -0
- venv/Lib/site-packages/pip/_internal/metadata/importlib/__init__.py +4 -0
- venv/Lib/site-packages/pip/_internal/metadata/importlib/_compat.py +55 -0
- venv/Lib/site-packages/pip/_internal/metadata/importlib/_dists.py +224 -0
- venv/Lib/site-packages/pip/_internal/metadata/importlib/_envs.py +188 -0
- venv/Lib/site-packages/pip/_internal/metadata/pkg_resources.py +270 -0
- venv/Lib/site-packages/pip/_internal/models/__init__.py +2 -0
- venv/Lib/site-packages/pip/_internal/models/candidate.py +34 -0
- venv/Lib/site-packages/pip/_internal/models/direct_url.py +237 -0
- venv/Lib/site-packages/pip/_internal/models/format_control.py +80 -0
- venv/Lib/site-packages/pip/_internal/models/index.py +28 -0
- venv/Lib/site-packages/pip/_internal/models/installation_report.py +53 -0
- venv/Lib/site-packages/pip/_internal/models/link.py +581 -0
- venv/Lib/site-packages/pip/_internal/models/scheme.py +31 -0
- venv/Lib/site-packages/pip/_internal/models/search_scope.py +132 -0
- venv/Lib/site-packages/pip/_internal/models/selection_prefs.py +51 -0
- venv/Lib/site-packages/pip/_internal/models/target_python.py +110 -0
- venv/Lib/site-packages/pip/_internal/models/wheel.py +92 -0
- venv/Lib/site-packages/pip/_internal/network/__init__.py +2 -0
- venv/Lib/site-packages/pip/_internal/network/auth.py +561 -0
- venv/Lib/site-packages/pip/_internal/network/cache.py +69 -0
- venv/Lib/site-packages/pip/_internal/network/download.py +186 -0
- venv/Lib/site-packages/pip/_internal/network/lazy_wheel.py +210 -0
- venv/Lib/site-packages/pip/_internal/network/session.py +519 -0
- venv/Lib/site-packages/pip/_internal/network/utils.py +96 -0
- venv/Lib/site-packages/pip/_internal/network/xmlrpc.py +60 -0
- venv/Lib/site-packages/pip/_internal/operations/__init__.py +0 -0
- venv/Lib/site-packages/pip/_internal/operations/build/__init__.py +0 -0
- venv/Lib/site-packages/pip/_internal/operations/build/build_tracker.py +124 -0
- venv/Lib/site-packages/pip/_internal/operations/build/metadata.py +39 -0
- venv/Lib/site-packages/pip/_internal/operations/build/metadata_editable.py +41 -0
- venv/Lib/site-packages/pip/_internal/operations/build/metadata_legacy.py +74 -0
- venv/Lib/site-packages/pip/_internal/operations/build/wheel.py +37 -0
- venv/Lib/site-packages/pip/_internal/operations/build/wheel_editable.py +46 -0
- venv/Lib/site-packages/pip/_internal/operations/build/wheel_legacy.py +102 -0
- venv/Lib/site-packages/pip/_internal/operations/check.py +187 -0
- venv/Lib/site-packages/pip/_internal/operations/freeze.py +255 -0
- venv/Lib/site-packages/pip/_internal/operations/install/__init__.py +2 -0
- venv/Lib/site-packages/pip/_internal/operations/install/editable_legacy.py +46 -0
- venv/Lib/site-packages/pip/_internal/operations/install/wheel.py +740 -0
- venv/Lib/site-packages/pip/_internal/operations/prepare.py +743 -0
- venv/Lib/site-packages/pip/_internal/pyproject.py +179 -0
- venv/Lib/site-packages/pip/_internal/req/__init__.py +92 -0
- venv/Lib/site-packages/pip/_internal/req/constructors.py +506 -0
- venv/Lib/site-packages/pip/_internal/req/req_file.py +552 -0
- venv/Lib/site-packages/pip/_internal/req/req_install.py +874 -0
- venv/Lib/site-packages/pip/_internal/req/req_set.py +119 -0
- venv/Lib/site-packages/pip/_internal/req/req_uninstall.py +650 -0
- venv/Lib/site-packages/pip/_internal/resolution/__init__.py +0 -0
- venv/Lib/site-packages/pip/_internal/resolution/base.py +20 -0
- venv/Lib/site-packages/pip/_internal/resolution/legacy/__init__.py +0 -0
- venv/Lib/site-packages/pip/_internal/resolution/legacy/resolver.py +600 -0
- venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__init__.py +0 -0
- venv/Lib/site-packages/pip/_internal/resolution/resolvelib/base.py +141 -0
- venv/Lib/site-packages/pip/_internal/resolution/resolvelib/candidates.py +555 -0
- venv/Lib/site-packages/pip/_internal/resolution/resolvelib/factory.py +730 -0
- venv/Lib/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py +155 -0
- venv/Lib/site-packages/pip/_internal/resolution/resolvelib/provider.py +255 -0
- venv/Lib/site-packages/pip/_internal/resolution/resolvelib/reporter.py +80 -0
- venv/Lib/site-packages/pip/_internal/resolution/resolvelib/requirements.py +165 -0
- venv/Lib/site-packages/pip/_internal/resolution/resolvelib/resolver.py +299 -0
- venv/Lib/site-packages/pip/_internal/self_outdated_check.py +242 -0
- venv/Lib/site-packages/pip/_internal/utils/__init__.py +0 -0
- venv/Lib/site-packages/pip/_internal/utils/_jaraco_text.py +109 -0
- venv/Lib/site-packages/pip/_internal/utils/_log.py +38 -0
- venv/Lib/site-packages/pip/_internal/utils/appdirs.py +52 -0
- venv/Lib/site-packages/pip/_internal/utils/compat.py +63 -0
- venv/Lib/site-packages/pip/_internal/utils/compatibility_tags.py +165 -0
- venv/Lib/site-packages/pip/_internal/utils/datetime.py +11 -0
- venv/Lib/site-packages/pip/_internal/utils/deprecation.py +120 -0
- venv/Lib/site-packages/pip/_internal/utils/direct_url_helpers.py +87 -0
- venv/Lib/site-packages/pip/_internal/utils/egg_link.py +72 -0
- venv/Lib/site-packages/pip/_internal/utils/encoding.py +36 -0
- venv/Lib/site-packages/pip/_internal/utils/entrypoints.py +84 -0
- venv/Lib/site-packages/pip/_internal/utils/filesystem.py +153 -0
- venv/Lib/site-packages/pip/_internal/utils/filetypes.py +27 -0
- venv/Lib/site-packages/pip/_internal/utils/glibc.py +88 -0
- venv/Lib/site-packages/pip/_internal/utils/hashes.py +151 -0
- venv/Lib/site-packages/pip/_internal/utils/inject_securetransport.py +35 -0
- venv/Lib/site-packages/pip/_internal/utils/logging.py +348 -0
- venv/Lib/site-packages/pip/_internal/utils/misc.py +735 -0
- venv/Lib/site-packages/pip/_internal/utils/models.py +39 -0
- venv/Lib/site-packages/pip/_internal/utils/packaging.py +57 -0
- venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py +146 -0
- venv/Lib/site-packages/pip/_internal/utils/subprocess.py +260 -0
- venv/Lib/site-packages/pip/_internal/utils/temp_dir.py +246 -0
- venv/Lib/site-packages/pip/_internal/utils/unpacking.py +257 -0
- venv/Lib/site-packages/pip/_internal/utils/urls.py +62 -0
- venv/Lib/site-packages/pip/_internal/utils/virtualenv.py +104 -0
- venv/Lib/site-packages/pip/_internal/utils/wheel.py +136 -0
- venv/Lib/site-packages/pip/_internal/vcs/__init__.py +15 -0
- venv/Lib/site-packages/pip/_internal/vcs/bazaar.py +112 -0
- venv/Lib/site-packages/pip/_internal/vcs/git.py +526 -0
- venv/Lib/site-packages/pip/_internal/vcs/mercurial.py +163 -0
- venv/Lib/site-packages/pip/_internal/vcs/subversion.py +324 -0
- venv/Lib/site-packages/pip/_internal/vcs/versioncontrol.py +705 -0
- venv/Lib/site-packages/pip/_internal/wheel_builder.py +355 -0
- venv/Lib/site-packages/pip/_vendor/__init__.py +120 -0
- venv/Lib/site-packages/pip/_vendor/cachecontrol/__init__.py +18 -0
- venv/Lib/site-packages/pip/_vendor/cachecontrol/_cmd.py +61 -0
- venv/Lib/site-packages/pip/_vendor/cachecontrol/adapter.py +137 -0
- venv/Lib/site-packages/pip/_vendor/cachecontrol/cache.py +65 -0
- venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__init__.py +9 -0
- venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py +188 -0
- venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py +39 -0
- venv/Lib/site-packages/pip/_vendor/cachecontrol/compat.py +32 -0
- venv/Lib/site-packages/pip/_vendor/cachecontrol/controller.py +439 -0
- venv/Lib/site-packages/pip/_vendor/cachecontrol/filewrapper.py +111 -0
- venv/Lib/site-packages/pip/_vendor/cachecontrol/heuristics.py +139 -0
- venv/Lib/site-packages/pip/_vendor/cachecontrol/serialize.py +190 -0
- venv/Lib/site-packages/pip/_vendor/cachecontrol/wrapper.py +33 -0
- venv/Lib/site-packages/pip/_vendor/certifi/__init__.py +4 -0
- venv/Lib/site-packages/pip/_vendor/certifi/__main__.py +12 -0
- venv/Lib/site-packages/pip/_vendor/certifi/core.py +108 -0
- venv/Lib/site-packages/pip/_vendor/chardet/__init__.py +115 -0
- venv/Lib/site-packages/pip/_vendor/chardet/big5freq.py +386 -0
- venv/Lib/site-packages/pip/_vendor/chardet/big5prober.py +47 -0
- venv/Lib/site-packages/pip/_vendor/chardet/chardistribution.py +261 -0
- venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py +106 -0
- venv/Lib/site-packages/pip/_vendor/chardet/charsetprober.py +147 -0
- venv/Lib/site-packages/pip/_vendor/chardet/cli/__init__.py +0 -0
- venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py +112 -0
- venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py +90 -0
- venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachinedict.py +19 -0
- venv/Lib/site-packages/pip/_vendor/chardet/cp949prober.py +49 -0
- venv/Lib/site-packages/pip/_vendor/chardet/enums.py +85 -0
- venv/Lib/site-packages/pip/_vendor/chardet/escprober.py +102 -0
- venv/Lib/site-packages/pip/_vendor/chardet/escsm.py +261 -0
- venv/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py +102 -0
- venv/Lib/site-packages/pip/_vendor/chardet/euckrfreq.py +196 -0
- venv/Lib/site-packages/pip/_vendor/chardet/euckrprober.py +47 -0
- venv/Lib/site-packages/pip/_vendor/chardet/euctwfreq.py +388 -0
- venv/Lib/site-packages/pip/_vendor/chardet/euctwprober.py +47 -0
- venv/Lib/site-packages/pip/_vendor/chardet/gb2312freq.py +284 -0
- venv/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py +47 -0
- venv/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py +316 -0
- venv/Lib/site-packages/pip/_vendor/chardet/jisfreq.py +325 -0
- venv/Lib/site-packages/pip/_vendor/chardet/johabfreq.py +2382 -0
- venv/Lib/site-packages/pip/_vendor/chardet/johabprober.py +47 -0
- venv/Lib/site-packages/pip/_vendor/chardet/jpcntx.py +238 -0
- venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py +4649 -0
- venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py +4397 -0
- venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py +4380 -0
- venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py +4649 -0
- venv/Lib/site-packages/pip/_vendor/chardet/langrussianmodel.py +5725 -0
- venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py +4380 -0
- venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py +4380 -0
- venv/Lib/site-packages/pip/_vendor/chardet/latin1prober.py +147 -0
- venv/Lib/site-packages/pip/_vendor/chardet/macromanprober.py +162 -0
- venv/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py +95 -0
- venv/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py +57 -0
- venv/Lib/site-packages/pip/_vendor/chardet/mbcssm.py +661 -0
- venv/Lib/site-packages/pip/_vendor/chardet/metadata/__init__.py +0 -0
- venv/Lib/site-packages/pip/_vendor/chardet/metadata/languages.py +352 -0
- venv/Lib/site-packages/pip/_vendor/chardet/resultdict.py +16 -0
- venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py +162 -0
- venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py +88 -0
- venv/Lib/site-packages/pip/_vendor/chardet/sjisprober.py +105 -0
- venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py +362 -0
- venv/Lib/site-packages/pip/_vendor/chardet/utf1632prober.py +225 -0
- venv/Lib/site-packages/pip/_vendor/chardet/utf8prober.py +82 -0
- venv/Lib/site-packages/pip/_vendor/chardet/version.py +9 -0
- venv/Lib/site-packages/pip/_vendor/colorama/__init__.py +7 -0
- venv/Lib/site-packages/pip/_vendor/colorama/ansi.py +102 -0
- venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py +277 -0
- venv/Lib/site-packages/pip/_vendor/colorama/initialise.py +121 -0
- venv/Lib/site-packages/pip/_vendor/colorama/tests/__init__.py +1 -0
- venv/Lib/site-packages/pip/_vendor/colorama/tests/ansi_test.py +76 -0
- venv/Lib/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py +294 -0
- venv/Lib/site-packages/pip/_vendor/colorama/tests/initialise_test.py +189 -0
- venv/Lib/site-packages/pip/_vendor/colorama/tests/isatty_test.py +57 -0
- venv/Lib/site-packages/pip/_vendor/colorama/tests/utils.py +49 -0
- venv/Lib/site-packages/pip/_vendor/colorama/tests/winterm_test.py +131 -0
- venv/Lib/site-packages/pip/_vendor/colorama/win32.py +180 -0
- venv/Lib/site-packages/pip/_vendor/colorama/winterm.py +195 -0
- venv/Lib/site-packages/pip/_vendor/distlib/__init__.py +23 -0
- venv/Lib/site-packages/pip/_vendor/distlib/compat.py +1116 -0
- venv/Lib/site-packages/pip/_vendor/distlib/database.py +1350 -0
- venv/Lib/site-packages/pip/_vendor/distlib/index.py +508 -0
- venv/Lib/site-packages/pip/_vendor/distlib/locators.py +1300 -0
- venv/Lib/site-packages/pip/_vendor/distlib/manifest.py +393 -0
- venv/Lib/site-packages/pip/_vendor/distlib/markers.py +152 -0
- venv/Lib/site-packages/pip/_vendor/distlib/metadata.py +1076 -0
- venv/Lib/site-packages/pip/_vendor/distlib/resources.py +358 -0
- venv/Lib/site-packages/pip/_vendor/distlib/scripts.py +437 -0
- venv/Lib/site-packages/pip/_vendor/distlib/util.py +1932 -0
- venv/Lib/site-packages/pip/_vendor/distlib/version.py +739 -0
- venv/Lib/site-packages/pip/_vendor/distlib/wheel.py +1082 -0
- venv/Lib/site-packages/pip/_vendor/distro/__init__.py +54 -0
- venv/Lib/site-packages/pip/_vendor/distro/__main__.py +4 -0
- venv/Lib/site-packages/pip/_vendor/distro/distro.py +1399 -0
- venv/Lib/site-packages/pip/_vendor/idna/__init__.py +44 -0
- venv/Lib/site-packages/pip/_vendor/idna/codec.py +112 -0
- venv/Lib/site-packages/pip/_vendor/idna/compat.py +13 -0
- venv/Lib/site-packages/pip/_vendor/idna/core.py +400 -0
- venv/Lib/site-packages/pip/_vendor/idna/idnadata.py +2151 -0
- venv/Lib/site-packages/pip/_vendor/idna/intranges.py +54 -0
- venv/Lib/site-packages/pip/_vendor/idna/package_data.py +2 -0
- venv/Lib/site-packages/pip/_vendor/idna/uts46data.py +8600 -0
- venv/Lib/site-packages/pip/_vendor/msgpack/__init__.py +57 -0
- venv/Lib/site-packages/pip/_vendor/msgpack/exceptions.py +48 -0
- venv/Lib/site-packages/pip/_vendor/msgpack/ext.py +193 -0
- venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py +1010 -0
- venv/Lib/site-packages/pip/_vendor/packaging/__about__.py +26 -0
- venv/Lib/site-packages/pip/_vendor/packaging/__init__.py +25 -0
- venv/Lib/site-packages/pip/_vendor/packaging/_manylinux.py +301 -0
- venv/Lib/site-packages/pip/_vendor/packaging/_musllinux.py +136 -0
- venv/Lib/site-packages/pip/_vendor/packaging/_structures.py +61 -0
- venv/Lib/site-packages/pip/_vendor/packaging/markers.py +304 -0
- venv/Lib/site-packages/pip/_vendor/packaging/requirements.py +146 -0
- venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py +802 -0
- venv/Lib/site-packages/pip/_vendor/packaging/tags.py +487 -0
- venv/Lib/site-packages/pip/_vendor/packaging/utils.py +136 -0
- venv/Lib/site-packages/pip/_vendor/packaging/version.py +504 -0
- venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py +3361 -0
- venv/Lib/site-packages/pip/_vendor/platformdirs/__init__.py +566 -0
- venv/Lib/site-packages/pip/_vendor/platformdirs/__main__.py +53 -0
- venv/Lib/site-packages/pip/_vendor/platformdirs/android.py +210 -0
- venv/Lib/site-packages/pip/_vendor/platformdirs/api.py +223 -0
- venv/Lib/site-packages/pip/_vendor/platformdirs/macos.py +91 -0
- venv/Lib/site-packages/pip/_vendor/platformdirs/unix.py +223 -0
- venv/Lib/site-packages/pip/_vendor/platformdirs/version.py +4 -0
- venv/Lib/site-packages/pip/_vendor/platformdirs/windows.py +255 -0
- venv/Lib/site-packages/pip/_vendor/pygments/__init__.py +82 -0
- venv/Lib/site-packages/pip/_vendor/pygments/__main__.py +17 -0
- venv/Lib/site-packages/pip/_vendor/pygments/cmdline.py +668 -0
- venv/Lib/site-packages/pip/_vendor/pygments/console.py +70 -0
- venv/Lib/site-packages/pip/_vendor/pygments/filter.py +71 -0
- venv/Lib/site-packages/pip/_vendor/pygments/filters/__init__.py +940 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatter.py +124 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/__init__.py +158 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/_mapping.py +23 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/bbcode.py +108 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/groff.py +170 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/html.py +989 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/img.py +645 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/irc.py +154 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/latex.py +521 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/other.py +161 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/pangomarkup.py +83 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/rtf.py +146 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/svg.py +188 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/terminal.py +127 -0
- venv/Lib/site-packages/pip/_vendor/pygments/formatters/terminal256.py +338 -0
- venv/Lib/site-packages/pip/_vendor/pygments/lexer.py +943 -0
- venv/Lib/site-packages/pip/_vendor/pygments/lexers/__init__.py +362 -0
- venv/Lib/site-packages/pip/_vendor/pygments/lexers/_mapping.py +559 -0
- venv/Lib/site-packages/pip/_vendor/pygments/lexers/python.py +1198 -0
- venv/Lib/site-packages/pip/_vendor/pygments/modeline.py +43 -0
- venv/Lib/site-packages/pip/_vendor/pygments/plugin.py +88 -0
- venv/Lib/site-packages/pip/_vendor/pygments/regexopt.py +91 -0
- venv/Lib/site-packages/pip/_vendor/pygments/scanner.py +104 -0
- venv/Lib/site-packages/pip/_vendor/pygments/sphinxext.py +217 -0
- venv/Lib/site-packages/pip/_vendor/pygments/style.py +197 -0
- venv/Lib/site-packages/pip/_vendor/pygments/styles/__init__.py +103 -0
- venv/Lib/site-packages/pip/_vendor/pygments/token.py +213 -0
- venv/Lib/site-packages/pip/_vendor/pygments/unistring.py +153 -0
- venv/Lib/site-packages/pip/_vendor/pygments/util.py +330 -0
- venv/Lib/site-packages/pip/_vendor/pyparsing/__init__.py +322 -0
- venv/Lib/site-packages/pip/_vendor/pyparsing/actions.py +217 -0
- venv/Lib/site-packages/pip/_vendor/pyparsing/common.py +432 -0
- venv/Lib/site-packages/pip/_vendor/pyparsing/core.py +6115 -0
- venv/Lib/site-packages/pip/_vendor/pyparsing/diagram/__init__.py +656 -0
- venv/Lib/site-packages/pip/_vendor/pyparsing/exceptions.py +299 -0
- venv/Lib/site-packages/pip/_vendor/pyparsing/helpers.py +1100 -0
- venv/Lib/site-packages/pip/_vendor/pyparsing/results.py +796 -0
- venv/Lib/site-packages/pip/_vendor/pyparsing/testing.py +331 -0
- venv/Lib/site-packages/pip/_vendor/pyparsing/unicode.py +361 -0
- venv/Lib/site-packages/pip/_vendor/pyparsing/util.py +284 -0
- venv/Lib/site-packages/pip/_vendor/pyproject_hooks/__init__.py +23 -0
- venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_compat.py +8 -0
- venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_impl.py +330 -0
- venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py +18 -0
- venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py +353 -0
- venv/Lib/site-packages/pip/_vendor/requests/__init__.py +182 -0
- venv/Lib/site-packages/pip/_vendor/requests/__version__.py +14 -0
- venv/Lib/site-packages/pip/_vendor/requests/_internal_utils.py +50 -0
- venv/Lib/site-packages/pip/_vendor/requests/adapters.py +538 -0
- venv/Lib/site-packages/pip/_vendor/requests/api.py +157 -0
- venv/Lib/site-packages/pip/_vendor/requests/auth.py +315 -0
- venv/Lib/site-packages/pip/_vendor/requests/certs.py +24 -0
- venv/Lib/site-packages/pip/_vendor/requests/compat.py +67 -0
- venv/Lib/site-packages/pip/_vendor/requests/cookies.py +561 -0
- venv/Lib/site-packages/pip/_vendor/requests/exceptions.py +141 -0
- venv/Lib/site-packages/pip/_vendor/requests/help.py +131 -0
- venv/Lib/site-packages/pip/_vendor/requests/hooks.py +33 -0
- venv/Lib/site-packages/pip/_vendor/requests/models.py +1034 -0
- venv/Lib/site-packages/pip/_vendor/requests/packages.py +16 -0
- venv/Lib/site-packages/pip/_vendor/requests/sessions.py +833 -0
- venv/Lib/site-packages/pip/_vendor/requests/status_codes.py +128 -0
- venv/Lib/site-packages/pip/_vendor/requests/structures.py +99 -0
- venv/Lib/site-packages/pip/_vendor/requests/utils.py +1094 -0
- venv/Lib/site-packages/pip/_vendor/resolvelib/__init__.py +26 -0
- venv/Lib/site-packages/pip/_vendor/resolvelib/compat/__init__.py +0 -0
- venv/Lib/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py +6 -0
- venv/Lib/site-packages/pip/_vendor/resolvelib/providers.py +133 -0
- venv/Lib/site-packages/pip/_vendor/resolvelib/reporters.py +43 -0
- venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers.py +547 -0
- venv/Lib/site-packages/pip/_vendor/resolvelib/structs.py +170 -0
- venv/Lib/site-packages/pip/_vendor/rich/__init__.py +177 -0
- venv/Lib/site-packages/pip/_vendor/rich/__main__.py +274 -0
- venv/Lib/site-packages/pip/_vendor/rich/_cell_widths.py +451 -0
- venv/Lib/site-packages/pip/_vendor/rich/_emoji_codes.py +3610 -0
- venv/Lib/site-packages/pip/_vendor/rich/_emoji_replace.py +32 -0
- venv/Lib/site-packages/pip/_vendor/rich/_export_format.py +76 -0
- venv/Lib/site-packages/pip/_vendor/rich/_extension.py +10 -0
- venv/Lib/site-packages/pip/_vendor/rich/_fileno.py +24 -0
- venv/Lib/site-packages/pip/_vendor/rich/_inspect.py +270 -0
- venv/Lib/site-packages/pip/_vendor/rich/_log_render.py +94 -0
- venv/Lib/site-packages/pip/_vendor/rich/_loop.py +43 -0
- venv/Lib/site-packages/pip/_vendor/rich/_null_file.py +69 -0
- venv/Lib/site-packages/pip/_vendor/rich/_palettes.py +309 -0
- venv/Lib/site-packages/pip/_vendor/rich/_pick.py +17 -0
- venv/Lib/site-packages/pip/_vendor/rich/_ratio.py +160 -0
- venv/Lib/site-packages/pip/_vendor/rich/_spinners.py +482 -0
- venv/Lib/site-packages/pip/_vendor/rich/_stack.py +16 -0
- venv/Lib/site-packages/pip/_vendor/rich/_timer.py +19 -0
- venv/Lib/site-packages/pip/_vendor/rich/_win32_console.py +662 -0
- venv/Lib/site-packages/pip/_vendor/rich/_windows.py +72 -0
- venv/Lib/site-packages/pip/_vendor/rich/_windows_renderer.py +56 -0
- venv/Lib/site-packages/pip/_vendor/rich/_wrap.py +56 -0
- venv/Lib/site-packages/pip/_vendor/rich/abc.py +33 -0
- venv/Lib/site-packages/pip/_vendor/rich/align.py +311 -0
- venv/Lib/site-packages/pip/_vendor/rich/ansi.py +240 -0
- venv/Lib/site-packages/pip/_vendor/rich/bar.py +94 -0
- venv/Lib/site-packages/pip/_vendor/rich/box.py +517 -0
- venv/Lib/site-packages/pip/_vendor/rich/cells.py +154 -0
- venv/Lib/site-packages/pip/_vendor/rich/color.py +622 -0
- venv/Lib/site-packages/pip/_vendor/rich/color_triplet.py +38 -0
- venv/Lib/site-packages/pip/_vendor/rich/columns.py +187 -0
- venv/Lib/site-packages/pip/_vendor/rich/console.py +2633 -0
- venv/Lib/site-packages/pip/_vendor/rich/constrain.py +37 -0
- venv/Lib/site-packages/pip/_vendor/rich/containers.py +167 -0
- venv/Lib/site-packages/pip/_vendor/rich/control.py +225 -0
- venv/Lib/site-packages/pip/_vendor/rich/default_styles.py +190 -0
- venv/Lib/site-packages/pip/_vendor/rich/diagnose.py +37 -0
- venv/Lib/site-packages/pip/_vendor/rich/emoji.py +96 -0
- venv/Lib/site-packages/pip/_vendor/rich/errors.py +34 -0
- venv/Lib/site-packages/pip/_vendor/rich/file_proxy.py +57 -0
- venv/Lib/site-packages/pip/_vendor/rich/filesize.py +89 -0
- venv/Lib/site-packages/pip/_vendor/rich/highlighter.py +232 -0
- venv/Lib/site-packages/pip/_vendor/rich/json.py +140 -0
- venv/Lib/site-packages/pip/_vendor/rich/jupyter.py +101 -0
- venv/Lib/site-packages/pip/_vendor/rich/layout.py +443 -0
- venv/Lib/site-packages/pip/_vendor/rich/live.py +375 -0
- venv/Lib/site-packages/pip/_vendor/rich/live_render.py +113 -0
- venv/Lib/site-packages/pip/_vendor/rich/logging.py +289 -0
- venv/Lib/site-packages/pip/_vendor/rich/markup.py +246 -0
- venv/Lib/site-packages/pip/_vendor/rich/measure.py +151 -0
- venv/Lib/site-packages/pip/_vendor/rich/padding.py +141 -0
- venv/Lib/site-packages/pip/_vendor/rich/pager.py +34 -0
- venv/Lib/site-packages/pip/_vendor/rich/palette.py +100 -0
- venv/Lib/site-packages/pip/_vendor/rich/panel.py +308 -0
- venv/Lib/site-packages/pip/_vendor/rich/pretty.py +994 -0
- venv/Lib/site-packages/pip/_vendor/rich/progress.py +1702 -0
- venv/Lib/site-packages/pip/_vendor/rich/progress_bar.py +224 -0
- venv/Lib/site-packages/pip/_vendor/rich/prompt.py +376 -0
- venv/Lib/site-packages/pip/_vendor/rich/protocol.py +42 -0
- venv/Lib/site-packages/pip/_vendor/rich/region.py +10 -0
- venv/Lib/site-packages/pip/_vendor/rich/repr.py +149 -0
- venv/Lib/site-packages/pip/_vendor/rich/rule.py +130 -0
- venv/Lib/site-packages/pip/_vendor/rich/scope.py +86 -0
- venv/Lib/site-packages/pip/_vendor/rich/screen.py +54 -0
- venv/Lib/site-packages/pip/_vendor/rich/segment.py +739 -0
- venv/Lib/site-packages/pip/_vendor/rich/spinner.py +137 -0
- venv/Lib/site-packages/pip/_vendor/rich/status.py +132 -0
- venv/Lib/site-packages/pip/_vendor/rich/style.py +796 -0
- venv/Lib/site-packages/pip/_vendor/rich/styled.py +42 -0
- venv/Lib/site-packages/pip/_vendor/rich/syntax.py +948 -0
- venv/Lib/site-packages/pip/_vendor/rich/table.py +1002 -0
- venv/Lib/site-packages/pip/_vendor/rich/terminal_theme.py +153 -0
- venv/Lib/site-packages/pip/_vendor/rich/text.py +1307 -0
- venv/Lib/site-packages/pip/_vendor/rich/theme.py +115 -0
- venv/Lib/site-packages/pip/_vendor/rich/themes.py +5 -0
- venv/Lib/site-packages/pip/_vendor/rich/traceback.py +756 -0
- venv/Lib/site-packages/pip/_vendor/rich/tree.py +251 -0
- venv/Lib/site-packages/pip/_vendor/six.py +998 -0
- venv/Lib/site-packages/pip/_vendor/tenacity/__init__.py +608 -0
- venv/Lib/site-packages/pip/_vendor/tenacity/_asyncio.py +94 -0
- venv/Lib/site-packages/pip/_vendor/tenacity/_utils.py +76 -0
- venv/Lib/site-packages/pip/_vendor/tenacity/after.py +51 -0
- venv/Lib/site-packages/pip/_vendor/tenacity/before.py +46 -0
- venv/Lib/site-packages/pip/_vendor/tenacity/before_sleep.py +71 -0
- venv/Lib/site-packages/pip/_vendor/tenacity/nap.py +43 -0
- venv/Lib/site-packages/pip/_vendor/tenacity/retry.py +272 -0
- venv/Lib/site-packages/pip/_vendor/tenacity/stop.py +103 -0
- venv/Lib/site-packages/pip/_vendor/tenacity/tornadoweb.py +59 -0
- venv/Lib/site-packages/pip/_vendor/tenacity/wait.py +228 -0
- venv/Lib/site-packages/pip/_vendor/tomli/__init__.py +11 -0
- venv/Lib/site-packages/pip/_vendor/tomli/_parser.py +691 -0
- venv/Lib/site-packages/pip/_vendor/tomli/_re.py +107 -0
- venv/Lib/site-packages/pip/_vendor/tomli/_types.py +10 -0
- venv/Lib/site-packages/pip/_vendor/typing_extensions.py +3072 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/__init__.py +102 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/_collections.py +337 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/_version.py +2 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/connection.py +572 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py +1132 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__init__.py +0 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py +36 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py +0 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py +519 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py +397 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py +314 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py +130 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py +518 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/contrib/securetransport.py +921 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/contrib/socks.py +216 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/exceptions.py +323 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/fields.py +274 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/filepost.py +98 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/packages/__init__.py +0 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py +0 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py +51 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/weakref_finalize.py +155 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/packages/six.py +1076 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/poolmanager.py +537 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/request.py +170 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/response.py +879 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/util/__init__.py +49 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/util/connection.py +149 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/util/proxy.py +57 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/util/queue.py +22 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/util/request.py +137 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/util/response.py +107 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/util/retry.py +620 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_.py +495 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py +159 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/util/ssltransport.py +221 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/util/timeout.py +271 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/util/url.py +435 -0
- venv/Lib/site-packages/pip/_vendor/urllib3/util/wait.py +152 -0
- venv/Lib/site-packages/pip/_vendor/webencodings/__init__.py +342 -0
- venv/Lib/site-packages/pip/_vendor/webencodings/labels.py +231 -0
- venv/Lib/site-packages/pip/_vendor/webencodings/mklabels.py +59 -0
- venv/Lib/site-packages/pip/_vendor/webencodings/tests.py +153 -0
- venv/Lib/site-packages/pip/_vendor/webencodings/x_user_defined.py +325 -0
- venv/Lib/site-packages/pip/py.typed +4 -0
- venv/Lib/site-packages/pkg_resources/__init__.py +3296 -0
- venv/Lib/site-packages/pkg_resources/_vendor/__init__.py +0 -0
- venv/Lib/site-packages/pkg_resources/_vendor/appdirs.py +608 -0
- venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__init__.py +36 -0
- venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_adapters.py +170 -0
- venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_common.py +104 -0
- venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_compat.py +98 -0
- venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_itertools.py +35 -0
- venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_legacy.py +121 -0
- venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/abc.py +137 -0
- venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/readers.py +122 -0
- venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/simple.py +116 -0
- venv/Lib/site-packages/pkg_resources/_vendor/jaraco/__init__.py +0 -0
- venv/Lib/site-packages/pkg_resources/_vendor/jaraco/context.py +213 -0
- venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools.py +525 -0
- venv/Lib/site-packages/pkg_resources/_vendor/jaraco/text/__init__.py +599 -0
- venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.py +4 -0
- venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.py +4316 -0
- venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.py +698 -0
- venv/Lib/site-packages/pkg_resources/_vendor/packaging/__about__.py +26 -0
- venv/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py +25 -0
- venv/Lib/site-packages/pkg_resources/_vendor/packaging/_manylinux.py +301 -0
- venv/Lib/site-packages/pkg_resources/_vendor/packaging/_musllinux.py +136 -0
- venv/Lib/site-packages/pkg_resources/_vendor/packaging/_structures.py +61 -0
- venv/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py +304 -0
- venv/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py +146 -0
- venv/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py +802 -0
- venv/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py +487 -0
- venv/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py +136 -0
- venv/Lib/site-packages/pkg_resources/_vendor/packaging/version.py +504 -0
- venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/__init__.py +331 -0
- venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/actions.py +207 -0
- venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/common.py +424 -0
- venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/core.py +5814 -0
- venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/diagram/__init__.py +642 -0
- venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/exceptions.py +267 -0
- venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/helpers.py +1088 -0
- venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/results.py +760 -0
- venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/testing.py +331 -0
- venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/unicode.py +352 -0
- venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/util.py +235 -0
- venv/Lib/site-packages/pkg_resources/_vendor/zipp.py +329 -0
- venv/Lib/site-packages/pkg_resources/extern/__init__.py +76 -0
- venv/Lib/site-packages/setuptools/__init__.py +247 -0
- venv/Lib/site-packages/setuptools/_deprecation_warning.py +7 -0
- venv/Lib/site-packages/setuptools/_distutils/__init__.py +24 -0
- venv/Lib/site-packages/setuptools/_distutils/_collections.py +56 -0
- venv/Lib/site-packages/setuptools/_distutils/_functools.py +20 -0
- venv/Lib/site-packages/setuptools/_distutils/_macos_compat.py +12 -0
- venv/Lib/site-packages/setuptools/_distutils/_msvccompiler.py +572 -0
- venv/Lib/site-packages/setuptools/_distutils/archive_util.py +280 -0
- venv/Lib/site-packages/setuptools/_distutils/bcppcompiler.py +408 -0
- venv/Lib/site-packages/setuptools/_distutils/ccompiler.py +1220 -0
- venv/Lib/site-packages/setuptools/_distutils/cmd.py +436 -0
- venv/Lib/site-packages/setuptools/_distutils/command/__init__.py +25 -0
- venv/Lib/site-packages/setuptools/_distutils/command/_framework_compat.py +55 -0
- venv/Lib/site-packages/setuptools/_distutils/command/bdist.py +157 -0
- venv/Lib/site-packages/setuptools/_distutils/command/bdist_dumb.py +144 -0
- venv/Lib/site-packages/setuptools/_distutils/command/bdist_rpm.py +615 -0
- venv/Lib/site-packages/setuptools/_distutils/command/build.py +153 -0
- venv/Lib/site-packages/setuptools/_distutils/command/build_clib.py +208 -0
- venv/Lib/site-packages/setuptools/_distutils/command/build_ext.py +787 -0
- venv/Lib/site-packages/setuptools/_distutils/command/build_py.py +407 -0
- venv/Lib/site-packages/setuptools/_distutils/command/build_scripts.py +173 -0
- venv/Lib/site-packages/setuptools/_distutils/command/check.py +151 -0
- venv/Lib/site-packages/setuptools/_distutils/command/clean.py +76 -0
- venv/Lib/site-packages/setuptools/_distutils/command/config.py +377 -0
- venv/Lib/site-packages/setuptools/_distutils/command/install.py +814 -0
- venv/Lib/site-packages/setuptools/_distutils/command/install_data.py +84 -0
- venv/Lib/site-packages/setuptools/_distutils/command/install_egg_info.py +91 -0
- venv/Lib/site-packages/setuptools/_distutils/command/install_headers.py +45 -0
- venv/Lib/site-packages/setuptools/_distutils/command/install_lib.py +238 -0
- venv/Lib/site-packages/setuptools/_distutils/command/install_scripts.py +61 -0
- venv/Lib/site-packages/setuptools/_distutils/command/py37compat.py +31 -0
- venv/Lib/site-packages/setuptools/_distutils/command/register.py +319 -0
- venv/Lib/site-packages/setuptools/_distutils/command/sdist.py +531 -0
- venv/Lib/site-packages/setuptools/_distutils/command/upload.py +205 -0
- venv/Lib/site-packages/setuptools/_distutils/config.py +139 -0
- venv/Lib/site-packages/setuptools/_distutils/core.py +291 -0
- venv/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py +364 -0
- venv/Lib/site-packages/setuptools/_distutils/debug.py +5 -0
- venv/Lib/site-packages/setuptools/_distutils/dep_util.py +96 -0
- venv/Lib/site-packages/setuptools/_distutils/dir_util.py +243 -0
- venv/Lib/site-packages/setuptools/_distutils/dist.py +1286 -0
- venv/Lib/site-packages/setuptools/_distutils/errors.py +127 -0
- venv/Lib/site-packages/setuptools/_distutils/extension.py +248 -0
- venv/Lib/site-packages/setuptools/_distutils/fancy_getopt.py +470 -0
- venv/Lib/site-packages/setuptools/_distutils/file_util.py +249 -0
- venv/Lib/site-packages/setuptools/_distutils/filelist.py +371 -0
- venv/Lib/site-packages/setuptools/_distutils/log.py +80 -0
- venv/Lib/site-packages/setuptools/_distutils/msvc9compiler.py +832 -0
- venv/Lib/site-packages/setuptools/_distutils/msvccompiler.py +695 -0
- venv/Lib/site-packages/setuptools/_distutils/py38compat.py +8 -0
- venv/Lib/site-packages/setuptools/_distutils/py39compat.py +22 -0
- venv/Lib/site-packages/setuptools/_distutils/spawn.py +109 -0
- venv/Lib/site-packages/setuptools/_distutils/sysconfig.py +558 -0
- venv/Lib/site-packages/setuptools/_distutils/text_file.py +287 -0
- venv/Lib/site-packages/setuptools/_distutils/unixccompiler.py +401 -0
- venv/Lib/site-packages/setuptools/_distutils/util.py +513 -0
- venv/Lib/site-packages/setuptools/_distutils/version.py +358 -0
- venv/Lib/site-packages/setuptools/_distutils/versionpredicate.py +175 -0
- venv/Lib/site-packages/setuptools/_entry_points.py +86 -0
- venv/Lib/site-packages/setuptools/_imp.py +82 -0
- venv/Lib/site-packages/setuptools/_importlib.py +47 -0
- venv/Lib/site-packages/setuptools/_itertools.py +23 -0
- venv/Lib/site-packages/setuptools/_path.py +29 -0
- venv/Lib/site-packages/setuptools/_reqs.py +19 -0
- venv/Lib/site-packages/setuptools/_vendor/__init__.py +0 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__init__.py +1047 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py +68 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_collections.py +30 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_compat.py +71 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_functools.py +104 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_itertools.py +73 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_meta.py +48 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_text.py +99 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__init__.py +36 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_adapters.py +170 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_common.py +104 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_compat.py +98 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_itertools.py +35 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_legacy.py +121 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_resources/abc.py +137 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_resources/readers.py +122 -0
- venv/Lib/site-packages/setuptools/_vendor/importlib_resources/simple.py +116 -0
- venv/Lib/site-packages/setuptools/_vendor/jaraco/__init__.py +0 -0
- venv/Lib/site-packages/setuptools/_vendor/jaraco/context.py +213 -0
- venv/Lib/site-packages/setuptools/_vendor/jaraco/functools.py +525 -0
- venv/Lib/site-packages/setuptools/_vendor/jaraco/text/__init__.py +599 -0
- venv/Lib/site-packages/setuptools/_vendor/more_itertools/__init__.py +4 -0
- venv/Lib/site-packages/setuptools/_vendor/more_itertools/more.py +3824 -0
- venv/Lib/site-packages/setuptools/_vendor/more_itertools/recipes.py +620 -0
- venv/Lib/site-packages/setuptools/_vendor/ordered_set.py +488 -0
- venv/Lib/site-packages/setuptools/_vendor/packaging/__about__.py +26 -0
- venv/Lib/site-packages/setuptools/_vendor/packaging/__init__.py +25 -0
- venv/Lib/site-packages/setuptools/_vendor/packaging/_manylinux.py +301 -0
- venv/Lib/site-packages/setuptools/_vendor/packaging/_musllinux.py +136 -0
- venv/Lib/site-packages/setuptools/_vendor/packaging/_structures.py +61 -0
- venv/Lib/site-packages/setuptools/_vendor/packaging/markers.py +304 -0
- venv/Lib/site-packages/setuptools/_vendor/packaging/requirements.py +146 -0
- venv/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py +802 -0
- venv/Lib/site-packages/setuptools/_vendor/packaging/tags.py +487 -0
- venv/Lib/site-packages/setuptools/_vendor/packaging/utils.py +136 -0
- venv/Lib/site-packages/setuptools/_vendor/packaging/version.py +504 -0
- venv/Lib/site-packages/setuptools/_vendor/pyparsing/__init__.py +331 -0
- venv/Lib/site-packages/setuptools/_vendor/pyparsing/actions.py +207 -0
- venv/Lib/site-packages/setuptools/_vendor/pyparsing/common.py +424 -0
- venv/Lib/site-packages/setuptools/_vendor/pyparsing/core.py +5814 -0
- venv/Lib/site-packages/setuptools/_vendor/pyparsing/diagram/__init__.py +642 -0
- venv/Lib/site-packages/setuptools/_vendor/pyparsing/exceptions.py +267 -0
- venv/Lib/site-packages/setuptools/_vendor/pyparsing/helpers.py +1088 -0
- venv/Lib/site-packages/setuptools/_vendor/pyparsing/results.py +760 -0
- venv/Lib/site-packages/setuptools/_vendor/pyparsing/testing.py +331 -0
- venv/Lib/site-packages/setuptools/_vendor/pyparsing/unicode.py +352 -0
- venv/Lib/site-packages/setuptools/_vendor/pyparsing/util.py +235 -0
- venv/Lib/site-packages/setuptools/_vendor/tomli/__init__.py +11 -0
- venv/Lib/site-packages/setuptools/_vendor/tomli/_parser.py +691 -0
- venv/Lib/site-packages/setuptools/_vendor/tomli/_re.py +107 -0
- venv/Lib/site-packages/setuptools/_vendor/tomli/_types.py +10 -0
- venv/Lib/site-packages/setuptools/_vendor/typing_extensions.py +2296 -0
- venv/Lib/site-packages/setuptools/_vendor/zipp.py +329 -0
- venv/Lib/site-packages/setuptools/archive_util.py +213 -0
- venv/Lib/site-packages/setuptools/build_meta.py +511 -0
- venv/Lib/site-packages/setuptools/command/__init__.py +12 -0
- venv/Lib/site-packages/setuptools/command/alias.py +78 -0
- venv/Lib/site-packages/setuptools/command/bdist_egg.py +457 -0
- venv/Lib/site-packages/setuptools/command/bdist_rpm.py +40 -0
- venv/Lib/site-packages/setuptools/command/build.py +146 -0
- venv/Lib/site-packages/setuptools/command/build_clib.py +101 -0
- venv/Lib/site-packages/setuptools/command/build_ext.py +383 -0
- venv/Lib/site-packages/setuptools/command/build_py.py +368 -0
- venv/Lib/site-packages/setuptools/command/develop.py +193 -0
- venv/Lib/site-packages/setuptools/command/dist_info.py +142 -0
- venv/Lib/site-packages/setuptools/command/easy_install.py +2312 -0
- venv/Lib/site-packages/setuptools/command/editable_wheel.py +844 -0
- venv/Lib/site-packages/setuptools/command/egg_info.py +763 -0
- venv/Lib/site-packages/setuptools/command/install.py +139 -0
- venv/Lib/site-packages/setuptools/command/install_egg_info.py +63 -0
- venv/Lib/site-packages/setuptools/command/install_lib.py +122 -0
- venv/Lib/site-packages/setuptools/command/install_scripts.py +70 -0
- venv/Lib/site-packages/setuptools/command/py36compat.py +134 -0
- venv/Lib/site-packages/setuptools/command/register.py +18 -0
- venv/Lib/site-packages/setuptools/command/rotate.py +64 -0
- venv/Lib/site-packages/setuptools/command/saveopts.py +22 -0
- venv/Lib/site-packages/setuptools/command/sdist.py +210 -0
- venv/Lib/site-packages/setuptools/command/setopt.py +149 -0
- venv/Lib/site-packages/setuptools/command/test.py +251 -0
- venv/Lib/site-packages/setuptools/command/upload.py +17 -0
- venv/Lib/site-packages/setuptools/command/upload_docs.py +213 -0
- venv/Lib/site-packages/setuptools/config/__init__.py +35 -0
- venv/Lib/site-packages/setuptools/config/_apply_pyprojecttoml.py +377 -0
- venv/Lib/site-packages/setuptools/config/_validate_pyproject/__init__.py +34 -0
- venv/Lib/site-packages/setuptools/config/_validate_pyproject/error_reporting.py +318 -0
- venv/Lib/site-packages/setuptools/config/_validate_pyproject/extra_validations.py +36 -0
- venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py +51 -0
- venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py +1035 -0
- venv/Lib/site-packages/setuptools/config/_validate_pyproject/formats.py +259 -0
- venv/Lib/site-packages/setuptools/config/expand.py +462 -0
- venv/Lib/site-packages/setuptools/config/pyprojecttoml.py +493 -0
- venv/Lib/site-packages/setuptools/config/setupcfg.py +762 -0
- venv/Lib/site-packages/setuptools/dep_util.py +25 -0
- venv/Lib/site-packages/setuptools/depends.py +176 -0
- venv/Lib/site-packages/setuptools/discovery.py +600 -0
- venv/Lib/site-packages/setuptools/dist.py +1222 -0
- venv/Lib/site-packages/setuptools/errors.py +58 -0
- venv/Lib/site-packages/setuptools/extension.py +148 -0
- venv/Lib/site-packages/setuptools/extern/__init__.py +76 -0
- venv/Lib/site-packages/setuptools/glob.py +167 -0
- venv/Lib/site-packages/setuptools/installer.py +104 -0
- venv/Lib/site-packages/setuptools/launch.py +36 -0
- venv/Lib/site-packages/setuptools/logging.py +36 -0
- venv/Lib/site-packages/setuptools/monkey.py +165 -0
- venv/Lib/site-packages/setuptools/msvc.py +1703 -0
- venv/Lib/site-packages/setuptools/namespaces.py +107 -0
- venv/Lib/site-packages/setuptools/package_index.py +1126 -0
- venv/Lib/site-packages/setuptools/py34compat.py +13 -0
- venv/Lib/site-packages/setuptools/sandbox.py +530 -0
- venv/Lib/site-packages/setuptools/unicode_utils.py +42 -0
- venv/Lib/site-packages/setuptools/version.py +6 -0
- venv/Lib/site-packages/setuptools/wheel.py +222 -0
- venv/Lib/site-packages/setuptools/windows_support.py +29 -0
- xplia/__init__.py +72 -0
- xplia/api/__init__.py +432 -0
- xplia/api/fastapi_app.py +453 -0
- xplia/cli.py +321 -0
- xplia/compliance/__init__.py +39 -0
- xplia/compliance/ai_act.py +538 -0
- xplia/compliance/compliance_checker.py +511 -0
- xplia/compliance/compliance_report.py +236 -0
- xplia/compliance/expert_review/__init__.py +18 -0
- xplia/compliance/expert_review/evaluation_criteria.py +209 -0
- xplia/compliance/expert_review/integration.py +270 -0
- xplia/compliance/expert_review/trust_expert_evaluator.py +379 -0
- xplia/compliance/explanation_rights.py +45 -0
- xplia/compliance/formatters/__init__.py +35 -0
- xplia/compliance/formatters/csv_formatter.py +179 -0
- xplia/compliance/formatters/html_formatter.py +689 -0
- xplia/compliance/formatters/html_trust_formatter.py +147 -0
- xplia/compliance/formatters/json_formatter.py +107 -0
- xplia/compliance/formatters/pdf_formatter.py +641 -0
- xplia/compliance/formatters/pdf_trust_formatter.py +309 -0
- xplia/compliance/formatters/trust_formatter_mixin.py +267 -0
- xplia/compliance/formatters/xml_formatter.py +173 -0
- xplia/compliance/gdpr.py +803 -0
- xplia/compliance/hipaa.py +134 -0
- xplia/compliance/report_base.py +205 -0
- xplia/compliance/report_generator.py +820 -0
- xplia/compliance/translations.py +299 -0
- xplia/core/__init__.py +98 -0
- xplia/core/base.py +391 -0
- xplia/core/config.py +297 -0
- xplia/core/factory.py +416 -0
- xplia/core/model_adapters/__init__.py +47 -0
- xplia/core/model_adapters/base.py +160 -0
- xplia/core/model_adapters/pytorch_adapter.py +339 -0
- xplia/core/model_adapters/sklearn_adapter.py +215 -0
- xplia/core/model_adapters/tensorflow_adapter.py +280 -0
- xplia/core/model_adapters/xgboost_adapter.py +295 -0
- xplia/core/optimizations.py +322 -0
- xplia/core/performance/__init__.py +57 -0
- xplia/core/performance/cache_manager.py +502 -0
- xplia/core/performance/memory_optimizer.py +465 -0
- xplia/core/performance/parallel_executor.py +327 -0
- xplia/core/registry.py +1234 -0
- xplia/explainers/__init__.py +70 -0
- xplia/explainers/__init__updated.py +62 -0
- xplia/explainers/adaptive/__init__.py +24 -0
- xplia/explainers/adaptive/explainer_selector.py +405 -0
- xplia/explainers/adaptive/explanation_quality.py +395 -0
- xplia/explainers/adaptive/fusion_strategies.py +297 -0
- xplia/explainers/adaptive/meta_explainer.py +320 -0
- xplia/explainers/adversarial/__init__.py +21 -0
- xplia/explainers/adversarial/adversarial_xai.py +678 -0
- xplia/explainers/anchor_explainer.py +1769 -0
- xplia/explainers/attention_explainer.py +996 -0
- xplia/explainers/bayesian/__init__.py +8 -0
- xplia/explainers/bayesian/bayesian_explainer.py +127 -0
- xplia/explainers/bias/__init__.py +17 -0
- xplia/explainers/bias/advanced_bias_detection.py +934 -0
- xplia/explainers/calibration/__init__.py +26 -0
- xplia/explainers/calibration/audience_adapter.py +376 -0
- xplia/explainers/calibration/audience_profiles.py +372 -0
- xplia/explainers/calibration/calibration_metrics.py +299 -0
- xplia/explainers/calibration/explanation_calibrator.py +460 -0
- xplia/explainers/causal/__init__.py +19 -0
- xplia/explainers/causal/causal_inference.py +669 -0
- xplia/explainers/certified/__init__.py +21 -0
- xplia/explainers/certified/certified_explanations.py +619 -0
- xplia/explainers/continual/__init__.py +8 -0
- xplia/explainers/continual/continual_explainer.py +102 -0
- xplia/explainers/counterfactual_explainer.py +804 -0
- xplia/explainers/counterfactuals/__init__.py +17 -0
- xplia/explainers/counterfactuals/advanced_counterfactuals.py +259 -0
- xplia/explainers/expert_evaluator.py +538 -0
- xplia/explainers/feature_importance_explainer.py +376 -0
- xplia/explainers/federated/__init__.py +17 -0
- xplia/explainers/federated/federated_xai.py +664 -0
- xplia/explainers/generative/__init__.py +15 -0
- xplia/explainers/generative/generative_explainer.py +243 -0
- xplia/explainers/gradient_explainer.py +3590 -0
- xplia/explainers/graph/__init__.py +26 -0
- xplia/explainers/graph/gnn_explainer.py +638 -0
- xplia/explainers/graph/molecular_explainer.py +438 -0
- xplia/explainers/lime_explainer.py +1580 -0
- xplia/explainers/llm/__init__.py +23 -0
- xplia/explainers/llm/llm_explainability.py +737 -0
- xplia/explainers/metalearning/__init__.py +15 -0
- xplia/explainers/metalearning/metalearning_explainer.py +276 -0
- xplia/explainers/moe/__init__.py +8 -0
- xplia/explainers/moe/moe_explainer.py +108 -0
- xplia/explainers/multimodal/__init__.py +30 -0
- xplia/explainers/multimodal/base.py +262 -0
- xplia/explainers/multimodal/diffusion_explainer.py +608 -0
- xplia/explainers/multimodal/foundation_model_explainer.py +323 -0
- xplia/explainers/multimodal/registry.py +139 -0
- xplia/explainers/multimodal/text_image_explainer.py +381 -0
- xplia/explainers/multimodal/vision_language_explainer.py +608 -0
- xplia/explainers/nas/__init__.py +5 -0
- xplia/explainers/nas/nas_explainer.py +65 -0
- xplia/explainers/neuralodes/__init__.py +5 -0
- xplia/explainers/neuralodes/neuralode_explainer.py +64 -0
- xplia/explainers/neurosymbolic/__init__.py +8 -0
- xplia/explainers/neurosymbolic/neurosymbolic_explainer.py +97 -0
- xplia/explainers/partial_dependence_explainer.py +509 -0
- xplia/explainers/privacy/__init__.py +21 -0
- xplia/explainers/privacy/differential_privacy_xai.py +624 -0
- xplia/explainers/quantum/__init__.py +5 -0
- xplia/explainers/quantum/quantum_explainer.py +79 -0
- xplia/explainers/recommender/__init__.py +8 -0
- xplia/explainers/recommender/recsys_explainer.py +124 -0
- xplia/explainers/reinforcement/__init__.py +15 -0
- xplia/explainers/reinforcement/rl_explainer.py +173 -0
- xplia/explainers/shap_explainer.py +2238 -0
- xplia/explainers/streaming/__init__.py +19 -0
- xplia/explainers/streaming/streaming_xai.py +703 -0
- xplia/explainers/timeseries/__init__.py +15 -0
- xplia/explainers/timeseries/timeseries_explainer.py +252 -0
- xplia/explainers/trust/__init__.py +24 -0
- xplia/explainers/trust/confidence_report.py +368 -0
- xplia/explainers/trust/fairwashing.py +489 -0
- xplia/explainers/trust/uncertainty.py +453 -0
- xplia/explainers/unified_explainer.py +566 -0
- xplia/explainers/unified_explainer_utils.py +309 -0
- xplia/integrations/__init__.py +3 -0
- xplia/integrations/mlflow_integration.py +331 -0
- xplia/integrations/wandb_integration.py +375 -0
- xplia/plugins/__init__.py +36 -0
- xplia/plugins/example_visualizer.py +11 -0
- xplia/utils/__init__.py +18 -0
- xplia/utils/performance.py +119 -0
- xplia/utils/validation.py +109 -0
- xplia/visualizations/__init__.py +31 -0
- xplia/visualizations/base.py +256 -0
- xplia/visualizations/boxplot_chart.py +224 -0
- xplia/visualizations/charts_impl.py +65 -0
- xplia/visualizations/gauge_chart.py +211 -0
- xplia/visualizations/gradient_viz.py +117 -0
- xplia/visualizations/heatmap_chart.py +173 -0
- xplia/visualizations/histogram_chart.py +176 -0
- xplia/visualizations/line_chart.py +100 -0
- xplia/visualizations/pie_chart.py +134 -0
- xplia/visualizations/radar_chart.py +154 -0
- xplia/visualizations/registry.py +76 -0
- xplia/visualizations/sankey_chart.py +190 -0
- xplia/visualizations/scatter_chart.py +252 -0
- xplia/visualizations/table_chart.py +263 -0
- xplia/visualizations/treemap_chart.py +216 -0
- xplia/visualizations.py +535 -0
- xplia/visualizers/__init__.py +87 -0
- xplia/visualizers/base_visualizer.py +294 -0
- xplia-1.0.1.dist-info/METADATA +685 -0
- xplia-1.0.1.dist-info/RECORD +870 -0
- xplia-1.0.1.dist-info/WHEEL +5 -0
- xplia-1.0.1.dist-info/entry_points.txt +2 -0
- xplia-1.0.1.dist-info/licenses/LICENSE +21 -0
- xplia-1.0.1.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,1580 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LIME Explainer pour XPLIA
|
|
3
|
+
========================
|
|
4
|
+
|
|
5
|
+
Ce module implémente l'intégration des explications LIME (Local Interpretable Model-agnostic Explanations)
|
|
6
|
+
dans le framework XPLIA. Cette technique permet d'expliquer les prédictions individuelles
|
|
7
|
+
en approximant localement le comportement du modèle avec un modèle interprétable.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
import pandas as pd
|
|
15
|
+
|
|
16
|
+
from ..core.base import (AudienceLevel, ExplainerBase, ExplainabilityMethod,
|
|
17
|
+
ExplanationResult, FeatureImportance, ModelMetadata)
|
|
18
|
+
from ..core.registry import register_explainer
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@register_explainer
|
|
22
|
+
class LimeExplainer(ExplainerBase):
|
|
23
|
+
"""
|
|
24
|
+
Explainer basé sur LIME (Local Interpretable Model-agnostic Explanations).
|
|
25
|
+
|
|
26
|
+
Cette classe fournit des explications de modèles basées sur l'approximation locale
|
|
27
|
+
du comportement du modèle par un modèle interprétable (généralement linéaire).
|
|
28
|
+
|
|
29
|
+
Caractéristiques principales:
|
|
30
|
+
- Support de différents types de données (tabular, text, image)
|
|
31
|
+
- Approximation locale par modèles interprétables
|
|
32
|
+
- Visualisations adaptées au niveau d'audience (technique, business, public)
|
|
33
|
+
- Optimisations de performance pour les grands jeux de données
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, model, **kwargs):
|
|
37
|
+
"""
|
|
38
|
+
Initialise l'explainer LIME.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
model: Modèle à expliquer
|
|
42
|
+
**kwargs: Paramètres additionnels
|
|
43
|
+
data_type: Type de données ('tabular', 'text', 'image')
|
|
44
|
+
feature_names: Noms des caractéristiques
|
|
45
|
+
class_names: Noms des classes
|
|
46
|
+
categorical_features: Indices des caractéristiques catégorielles
|
|
47
|
+
categorical_names: Noms des valeurs des caractéristiques catégorielles
|
|
48
|
+
kernel_width: Largeur du noyau pour la fonction de similarité
|
|
49
|
+
verbose: Niveau de verbosité
|
|
50
|
+
"""
|
|
51
|
+
super().__init__(model, **kwargs)
|
|
52
|
+
self._method = ExplainabilityMethod.LIME
|
|
53
|
+
self._supported_model_types = [
|
|
54
|
+
'RandomForestClassifier', 'RandomForestRegressor',
|
|
55
|
+
'GradientBoostingClassifier', 'GradientBoostingRegressor',
|
|
56
|
+
'XGBClassifier', 'XGBRegressor',
|
|
57
|
+
'LGBMClassifier', 'LGBMRegressor',
|
|
58
|
+
'CatBoostClassifier', 'CatBoostRegressor',
|
|
59
|
+
'LogisticRegression', 'LinearRegression',
|
|
60
|
+
'SVC', 'SVR',
|
|
61
|
+
'Sequential', 'Model', # Keras
|
|
62
|
+
'Module' # PyTorch
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
# Paramètres LIME
|
|
66
|
+
self._data_type = kwargs.get('data_type', 'tabular')
|
|
67
|
+
self._feature_names = kwargs.get('feature_names', None)
|
|
68
|
+
self._class_names = kwargs.get('class_names', None)
|
|
69
|
+
self._categorical_features = kwargs.get('categorical_features', [])
|
|
70
|
+
self._categorical_names = kwargs.get('categorical_names', {})
|
|
71
|
+
self._kernel_width = kwargs.get('kernel_width', None)
|
|
72
|
+
self._verbose = kwargs.get('verbose', False)
|
|
73
|
+
|
|
74
|
+
# Initialisation de l'explainer LIME
|
|
75
|
+
self._lime_explainer = None
|
|
76
|
+
self._initialize_explainer()
|
|
77
|
+
|
|
78
|
+
# Métadonnées du modèle
|
|
79
|
+
self._metadata = None
|
|
80
|
+
|
|
81
|
+
# Logger
|
|
82
|
+
self._logger = logging.getLogger(__name__)
|
|
83
|
+
|
|
84
|
+
def _initialize_explainer(self):
|
|
85
|
+
"""
|
|
86
|
+
Initialise l'explainer LIME approprié en fonction du type de données.
|
|
87
|
+
"""
|
|
88
|
+
try:
|
|
89
|
+
import lime
|
|
90
|
+
import lime.lime_tabular
|
|
91
|
+
import lime.lime_text
|
|
92
|
+
import lime.lime_image
|
|
93
|
+
except ImportError:
|
|
94
|
+
raise ImportError("Le package LIME est requis. Installez-le avec 'pip install lime'.")
|
|
95
|
+
|
|
96
|
+
# Tracer l'action
|
|
97
|
+
self.add_audit_record("initialize_explainer", {
|
|
98
|
+
"data_type": self._data_type,
|
|
99
|
+
"kernel_width": self._kernel_width,
|
|
100
|
+
"verbose": self._verbose
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
if self._data_type == 'tabular':
|
|
105
|
+
self._lime_explainer = lime.lime_tabular.LimeTabularExplainer(
|
|
106
|
+
training_data=np.array([]), # Sera remplacé lors de l'explication
|
|
107
|
+
feature_names=self._feature_names,
|
|
108
|
+
class_names=self._class_names,
|
|
109
|
+
categorical_features=self._categorical_features,
|
|
110
|
+
categorical_names=self._categorical_names,
|
|
111
|
+
kernel_width=self._kernel_width,
|
|
112
|
+
verbose=self._verbose,
|
|
113
|
+
mode='classification' if self._is_classifier() else 'regression'
|
|
114
|
+
)
|
|
115
|
+
self._logger.info("Explainer LIME tabular initialisé")
|
|
116
|
+
elif self._data_type == 'text':
|
|
117
|
+
self._lime_explainer = lime.lime_text.LimeTextExplainer(
|
|
118
|
+
class_names=self._class_names,
|
|
119
|
+
kernel_width=self._kernel_width,
|
|
120
|
+
verbose=self._verbose
|
|
121
|
+
)
|
|
122
|
+
self._logger.info("Explainer LIME text initialisé")
|
|
123
|
+
elif self._data_type == 'image':
|
|
124
|
+
self._lime_explainer = lime.lime_image.LimeImageExplainer(
|
|
125
|
+
kernel_width=self._kernel_width,
|
|
126
|
+
verbose=self._verbose
|
|
127
|
+
)
|
|
128
|
+
self._logger.info("Explainer LIME image initialisé")
|
|
129
|
+
else:
|
|
130
|
+
raise ValueError(f"Type de données non supporté: {self._data_type}. "
|
|
131
|
+
f"Utilisez 'tabular', 'text' ou 'image'.")
|
|
132
|
+
except Exception as e:
|
|
133
|
+
self._logger.error(f"Erreur lors de l'initialisation de l'explainer LIME: {str(e)}")
|
|
134
|
+
raise RuntimeError(f"Échec de l'initialisation de l'explainer LIME: {str(e)}")
|
|
135
|
+
|
|
136
|
+
def _is_classifier(self) -> bool:
|
|
137
|
+
"""
|
|
138
|
+
Détermine si le modèle est un classifieur ou un régresseur.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
bool: True si le modèle est un classifieur, False sinon
|
|
142
|
+
"""
|
|
143
|
+
# Vérifier les attributs spécifiques aux classifieurs
|
|
144
|
+
if hasattr(self._model, 'predict_proba'):
|
|
145
|
+
return True
|
|
146
|
+
if hasattr(self._model, 'classes_'):
|
|
147
|
+
return True
|
|
148
|
+
|
|
149
|
+
# Pour les modèles deep learning, vérifier la forme de sortie
|
|
150
|
+
model_module = self._model.__class__.__module__
|
|
151
|
+
if 'keras' in model_module or 'tensorflow' in model_module or 'torch' in model_module:
|
|
152
|
+
try:
|
|
153
|
+
# Créer des données synthétiques pour une inférence
|
|
154
|
+
input_shape = self._infer_model_input_shape()
|
|
155
|
+
if input_shape:
|
|
156
|
+
sample = np.random.normal(0, 0.1, size=(1, *input_shape))
|
|
157
|
+
preds = self._model_predict_wrapper(sample)
|
|
158
|
+
|
|
159
|
+
# Si la sortie a plusieurs dimensions et la dernière dimension > 1, c'est probablement une classification
|
|
160
|
+
if preds.ndim > 1 and preds.shape[-1] > 1:
|
|
161
|
+
return True
|
|
162
|
+
except:
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
# Par défaut, supposer que c'est un régresseur
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
def _model_predict_wrapper(self, instances):
|
|
169
|
+
"""
|
|
170
|
+
Wrapper pour standardiser les prédictions de différents frameworks ML.
|
|
171
|
+
|
|
172
|
+
Cette méthode détecte automatiquement le framework du modèle et adapte l'interface
|
|
173
|
+
de prédiction en conséquence, assurant une sortie unifiée quel que soit le framework.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
instances: Données d'entrée à prédire (ndarray, DataFrame, csr_matrix, etc.)
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
ndarray: Les prédictions standardisées (probs pour classification, valeurs pour régression)
|
|
180
|
+
"""
|
|
181
|
+
try:
|
|
182
|
+
# Détecter le framework du modèle
|
|
183
|
+
model_type = self._extract_model_type()
|
|
184
|
+
|
|
185
|
+
# Standardiser le format d'entrée si nécessaire
|
|
186
|
+
if hasattr(instances, 'values'):
|
|
187
|
+
# Convertir DataFrame en ndarray
|
|
188
|
+
instances = instances.values
|
|
189
|
+
|
|
190
|
+
# Adapter l'appel de prédiction selon le framework
|
|
191
|
+
if model_type == 'sklearn':
|
|
192
|
+
# sklearn a predict_proba pour classification, predict pour régression
|
|
193
|
+
if self._is_classifier() and hasattr(self._model, 'predict_proba'):
|
|
194
|
+
return self._model.predict_proba(instances)
|
|
195
|
+
else:
|
|
196
|
+
return self._model.predict(instances).reshape(-1, 1)
|
|
197
|
+
|
|
198
|
+
elif model_type in ['xgboost', 'lightgbm', 'catboost']:
|
|
199
|
+
# Ces frameworks ont souvent predict ou predict_proba
|
|
200
|
+
if hasattr(self._model, 'predict_proba'):
|
|
201
|
+
return self._model.predict_proba(instances)
|
|
202
|
+
else:
|
|
203
|
+
preds = self._model.predict(instances)
|
|
204
|
+
# S'assurer que les sorties sont 2D pour la cohérence
|
|
205
|
+
if preds.ndim == 1:
|
|
206
|
+
return preds.reshape(-1, 1)
|
|
207
|
+
return preds
|
|
208
|
+
|
|
209
|
+
elif model_type == 'tensorflow':
|
|
210
|
+
# TensorFlow/Keras models
|
|
211
|
+
import numpy as np
|
|
212
|
+
import tensorflow as tf
|
|
213
|
+
|
|
214
|
+
# Convertir en tenseur TF si nécessaire
|
|
215
|
+
if not isinstance(instances, tf.Tensor):
|
|
216
|
+
instances = tf.convert_to_tensor(instances, dtype=tf.float32)
|
|
217
|
+
|
|
218
|
+
# Prédire avec le modèle TF
|
|
219
|
+
preds = self._model(instances).numpy() if hasattr(self._model, '__call__') else \
|
|
220
|
+
self._model.predict(instances)
|
|
221
|
+
|
|
222
|
+
# Standardiser le format de sortie
|
|
223
|
+
return np.atleast_2d(preds)
|
|
224
|
+
|
|
225
|
+
elif model_type == 'pytorch':
|
|
226
|
+
# PyTorch models
|
|
227
|
+
import numpy as np
|
|
228
|
+
import torch
|
|
229
|
+
|
|
230
|
+
# Désactiver le calcul de gradient pour les prédictions
|
|
231
|
+
with torch.no_grad():
|
|
232
|
+
# Convertir en tenseur PyTorch si nécessaire
|
|
233
|
+
if not isinstance(instances, torch.Tensor):
|
|
234
|
+
instances = torch.tensor(instances, dtype=torch.float32)
|
|
235
|
+
|
|
236
|
+
# Mettre sur le device approprié (GPU/CPU)
|
|
237
|
+
if torch.cuda.is_available() and getattr(self, '_config', {}).get('use_gpu', False):
|
|
238
|
+
instances = instances.cuda()
|
|
239
|
+
if hasattr(self._model, 'cuda'):
|
|
240
|
+
model = self._model.cuda()
|
|
241
|
+
else:
|
|
242
|
+
model = self._model
|
|
243
|
+
else:
|
|
244
|
+
model = self._model
|
|
245
|
+
|
|
246
|
+
# Prédire et convertir en numpy
|
|
247
|
+
preds = model(instances) if hasattr(model, '__call__') else \
|
|
248
|
+
model.predict(instances)
|
|
249
|
+
preds = preds.cpu().numpy()
|
|
250
|
+
|
|
251
|
+
# Standardiser le format de sortie
|
|
252
|
+
return np.atleast_2d(preds)
|
|
253
|
+
else:
|
|
254
|
+
# Fallback générique pour les autres frameworks
|
|
255
|
+
preds = self._model.predict(instances)
|
|
256
|
+
# S'assurer que les sorties sont 2D pour la cohérence
|
|
257
|
+
if hasattr(preds, 'ndim') and preds.ndim == 1:
|
|
258
|
+
return preds.reshape(-1, 1)
|
|
259
|
+
return preds
|
|
260
|
+
|
|
261
|
+
except Exception as e:
|
|
262
|
+
self._logger.error(f"Erreur lors de la prédiction avec {self._model.__class__.__name__}: {str(e)}")
|
|
263
|
+
import traceback
|
|
264
|
+
self._logger.debug(traceback.format_exc())
|
|
265
|
+
raise RuntimeError(f"Échec de la prédiction: {str(e)}")
|
|
266
|
+
|
|
267
|
+
def _infer_model_input_shape(self):
|
|
268
|
+
"""
|
|
269
|
+
Tente de déduire la forme des entrées du modèle.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
tuple ou None: Forme déduite ou None si impossible
|
|
273
|
+
"""
|
|
274
|
+
model_module = self._model.__class__.__module__
|
|
275
|
+
|
|
276
|
+
if 'tensorflow' in model_module or 'keras' in model_module:
|
|
277
|
+
try:
|
|
278
|
+
# Pour les modèles Keras
|
|
279
|
+
return self._model.input_shape[1:]
|
|
280
|
+
except (AttributeError, IndexError):
|
|
281
|
+
pass
|
|
282
|
+
|
|
283
|
+
# Pour les modèles sklearn, xgboost, etc.
|
|
284
|
+
try:
|
|
285
|
+
if hasattr(self._model, 'n_features_in_'):
|
|
286
|
+
return (self._model.n_features_in_,)
|
|
287
|
+
except AttributeError:
|
|
288
|
+
pass
|
|
289
|
+
|
|
290
|
+
# Impossible de déduire
|
|
291
|
+
return None
|
|
292
|
+
|
|
293
|
+
def _model_predict_wrapper(self, x):
|
|
294
|
+
"""
|
|
295
|
+
Wrapper pour la fonction de prédiction du modèle, adapté pour LIME.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
x: Données d'entrée
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
numpy.ndarray: Prédictions du modèle
|
|
302
|
+
"""
|
|
303
|
+
try:
|
|
304
|
+
model_module = self._model.__class__.__module__
|
|
305
|
+
|
|
306
|
+
if 'sklearn' in model_module or 'xgboost' in model_module or 'lightgbm' in model_module:
|
|
307
|
+
if hasattr(self._model, 'predict_proba'):
|
|
308
|
+
return self._model.predict_proba(x)
|
|
309
|
+
else:
|
|
310
|
+
return self._model.predict(x)
|
|
311
|
+
elif 'tensorflow' in model_module or 'keras' in model_module:
|
|
312
|
+
# Conversion en format attendu par le modèle
|
|
313
|
+
if isinstance(x, pd.DataFrame):
|
|
314
|
+
x = x.values
|
|
315
|
+
|
|
316
|
+
# Appel au modèle
|
|
317
|
+
result = self._model.predict(x)
|
|
318
|
+
return result
|
|
319
|
+
elif 'torch' in model_module:
|
|
320
|
+
# Conversion en format attendu par le modèle
|
|
321
|
+
if isinstance(x, pd.DataFrame):
|
|
322
|
+
x = x.values
|
|
323
|
+
|
|
324
|
+
import torch
|
|
325
|
+
# Convertir en tensor PyTorch
|
|
326
|
+
if not isinstance(x, torch.Tensor):
|
|
327
|
+
x = torch.tensor(x, dtype=torch.float32)
|
|
328
|
+
|
|
329
|
+
# Mettre le modèle en mode évaluation
|
|
330
|
+
self._model.eval()
|
|
331
|
+
|
|
332
|
+
# Appel au modèle avec torch.no_grad
|
|
333
|
+
with torch.no_grad():
|
|
334
|
+
result = self._model(x)
|
|
335
|
+
|
|
336
|
+
# Conversion du résultat en numpy
|
|
337
|
+
if hasattr(result, 'detach'):
|
|
338
|
+
result = result.detach().numpy()
|
|
339
|
+
else:
|
|
340
|
+
result = result.numpy()
|
|
341
|
+
return result
|
|
342
|
+
else:
|
|
343
|
+
# Cas générique
|
|
344
|
+
if hasattr(self._model, 'predict'):
|
|
345
|
+
return self._model.predict(x)
|
|
346
|
+
else:
|
|
347
|
+
return self._model(x)
|
|
348
|
+
except Exception as e:
|
|
349
|
+
self._logger.error(f"Erreur dans le wrapper de prédiction: {str(e)}")
|
|
350
|
+
raise RuntimeError(f"Échec de la prédiction du modèle: {str(e)}")
|
|
351
|
+
|
|
352
|
+
def explain(self, X, y=None, **kwargs) -> ExplanationResult:
|
|
353
|
+
"""
|
|
354
|
+
Génère des explications LIME pour un ensemble de données.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
X: Données d'entrée à expliquer (DataFrame, numpy array, ou liste)
|
|
358
|
+
y: Valeurs cibles réelles (optionnel)
|
|
359
|
+
**kwargs: Paramètres additionnels
|
|
360
|
+
- num_features: Nombre de caractéristiques à inclure dans l'explication
|
|
361
|
+
- num_samples: Nombre d'échantillons à générer pour l'approximation locale
|
|
362
|
+
- max_instances: Nombre maximum d'instances à expliquer (pour performance)
|
|
363
|
+
- audience_level: Niveau d'audience (TECHNICAL, BUSINESS, PUBLIC)
|
|
364
|
+
- labels: Indices des classes à expliquer (pour les classifieurs multi-classes)
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
ExplanationResult: Résultat standardisé de l'explication
|
|
368
|
+
"""
|
|
369
|
+
if self._lime_explainer is None:
|
|
370
|
+
# Réinitialiser l'explainer avec les données actuelles
|
|
371
|
+
self._initialize_explainer_with_data(X, **kwargs)
|
|
372
|
+
|
|
373
|
+
# Paramètres
|
|
374
|
+
num_features = kwargs.get('num_features', 10)
|
|
375
|
+
num_samples = kwargs.get('num_samples', 5000)
|
|
376
|
+
max_instances = kwargs.get('max_instances', 100) # Limite pour performance
|
|
377
|
+
audience_level = kwargs.get('audience_level', AudienceLevel.TECHNICAL)
|
|
378
|
+
labels = kwargs.get('labels', None)
|
|
379
|
+
|
|
380
|
+
# Conversion des données en format approprié
|
|
381
|
+
if isinstance(X, pd.DataFrame):
|
|
382
|
+
feature_names = X.columns.tolist()
|
|
383
|
+
X_values = X.values
|
|
384
|
+
else:
|
|
385
|
+
X_values = np.array(X)
|
|
386
|
+
feature_names = kwargs.get('feature_names', self._feature_names) or \
|
|
387
|
+
[f"feature_{i}" for i in range(X_values.shape[1])]
|
|
388
|
+
|
|
389
|
+
# Limiter le nombre d'instances pour performance si nécessaire
|
|
390
|
+
if X_values.shape[0] > max_instances:
|
|
391
|
+
self._logger.warning(f"Échantillonnage de {max_instances} instances sur {X_values.shape[0]} pour performance.")
|
|
392
|
+
indices = np.random.choice(X_values.shape[0], max_instances, replace=False)
|
|
393
|
+
X_sample = X_values[indices]
|
|
394
|
+
else:
|
|
395
|
+
X_sample = X_values
|
|
396
|
+
|
|
397
|
+
# Tracer l'action
|
|
398
|
+
self.add_audit_record("explain", {
|
|
399
|
+
"n_samples": X_sample.shape[0],
|
|
400
|
+
"n_features": X_sample.shape[1],
|
|
401
|
+
"audience_level": audience_level.value if isinstance(audience_level, AudienceLevel) else audience_level,
|
|
402
|
+
"num_features": num_features,
|
|
403
|
+
"num_samples": num_samples
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
try:
|
|
407
|
+
# Calculer les explications LIME pour chaque instance
|
|
408
|
+
explanations = []
|
|
409
|
+
for i, instance in enumerate(X_sample):
|
|
410
|
+
if i > 0 and i % 10 == 0:
|
|
411
|
+
self._logger.info(f"Explication de l'instance {i}/{len(X_sample)}")
|
|
412
|
+
|
|
413
|
+
# Générer l'explication pour cette instance
|
|
414
|
+
if self._data_type == 'tabular':
|
|
415
|
+
exp = self._lime_explainer.explain_instance(
|
|
416
|
+
instance,
|
|
417
|
+
self._model_predict_wrapper,
|
|
418
|
+
num_features=num_features,
|
|
419
|
+
num_samples=num_samples,
|
|
420
|
+
labels=labels
|
|
421
|
+
)
|
|
422
|
+
elif self._data_type == 'text':
|
|
423
|
+
exp = self._lime_explainer.explain_instance(
|
|
424
|
+
instance,
|
|
425
|
+
self._model_predict_wrapper,
|
|
426
|
+
num_features=num_features,
|
|
427
|
+
labels=labels
|
|
428
|
+
)
|
|
429
|
+
elif self._data_type == 'image':
|
|
430
|
+
exp = self._lime_explainer.explain_instance(
|
|
431
|
+
instance,
|
|
432
|
+
self._model_predict_wrapper,
|
|
433
|
+
num_features=num_features,
|
|
434
|
+
labels=labels
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
explanations.append(exp)
|
|
438
|
+
|
|
439
|
+
# Agréger les importances de caractéristiques
|
|
440
|
+
feature_importances = self._aggregate_feature_importances(explanations, feature_names)
|
|
441
|
+
|
|
442
|
+
# Extraire les métadonnées du modèle
|
|
443
|
+
if not self._metadata:
|
|
444
|
+
self._extract_metadata()
|
|
445
|
+
|
|
446
|
+
# Créer le résultat d'explication
|
|
447
|
+
result = ExplanationResult(
|
|
448
|
+
method=ExplainabilityMethod.LIME,
|
|
449
|
+
model_metadata=self._metadata,
|
|
450
|
+
feature_importances=feature_importances,
|
|
451
|
+
raw_explanation={
|
|
452
|
+
"lime_explanations": explanations,
|
|
453
|
+
"feature_names": feature_names,
|
|
454
|
+
"data": X_sample
|
|
455
|
+
},
|
|
456
|
+
audience_level=audience_level
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
return result
|
|
460
|
+
|
|
461
|
+
except Exception as e:
|
|
462
|
+
self._logger.error(f"Erreur lors du calcul des explications LIME: {str(e)}")
|
|
463
|
+
raise RuntimeError(f"Échec de l'explication LIME: {str(e)}")
|
|
464
|
+
|
|
465
|
+
def _initialize_explainer_with_data(self, X, **kwargs):
|
|
466
|
+
"""
|
|
467
|
+
Réinitialise l'explainer LIME avec les données actuelles.
|
|
468
|
+
|
|
469
|
+
Args:
|
|
470
|
+
X: Données d'entrée pour initialiser l'explainer
|
|
471
|
+
**kwargs: Paramètres additionnels
|
|
472
|
+
"""
|
|
473
|
+
try:
|
|
474
|
+
import lime
|
|
475
|
+
import lime.lime_tabular
|
|
476
|
+
import lime.lime_text
|
|
477
|
+
import lime.lime_image
|
|
478
|
+
except ImportError:
|
|
479
|
+
raise ImportError("Le package LIME est requis. Installez-le avec 'pip install lime'.")
|
|
480
|
+
|
|
481
|
+
# Conversion des données en format approprié
|
|
482
|
+
if isinstance(X, pd.DataFrame):
|
|
483
|
+
feature_names = X.columns.tolist()
|
|
484
|
+
X_values = X.values
|
|
485
|
+
else:
|
|
486
|
+
X_values = np.array(X)
|
|
487
|
+
feature_names = kwargs.get('feature_names', self._feature_names) or \
|
|
488
|
+
[f"feature_{i}" for i in range(X_values.shape[1])]
|
|
489
|
+
|
|
490
|
+
# Mettre à jour les attributs
|
|
491
|
+
self._feature_names = feature_names
|
|
492
|
+
|
|
493
|
+
# Réinitialiser l'explainer avec les données
|
|
494
|
+
if self._data_type == 'tabular':
|
|
495
|
+
self._lime_explainer = lime.lime_tabular.LimeTabularExplainer(
|
|
496
|
+
training_data=X_values,
|
|
497
|
+
feature_names=feature_names,
|
|
498
|
+
class_names=self._class_names,
|
|
499
|
+
categorical_features=self._categorical_features,
|
|
500
|
+
categorical_names=self._categorical_names,
|
|
501
|
+
kernel_width=self._kernel_width,
|
|
502
|
+
verbose=self._verbose,
|
|
503
|
+
mode='classification' if self._is_classifier() else 'regression'
|
|
504
|
+
)
|
|
505
|
+
self._logger.info("Explainer LIME tabular réinitialisé avec les données")
|
|
506
|
+
else:
|
|
507
|
+
# Pour les types text et image, pas besoin de réinitialiser avec les données
|
|
508
|
+
pass
|
|
509
|
+
|
|
510
|
+
def _aggregate_feature_importances(self, explanations, feature_names):
|
|
511
|
+
"""
|
|
512
|
+
Agrège les importances de caractéristiques à partir des explications LIME.
|
|
513
|
+
|
|
514
|
+
Args:
|
|
515
|
+
explanations: Liste des explications LIME
|
|
516
|
+
feature_names: Noms des caractéristiques
|
|
517
|
+
|
|
518
|
+
Returns:
|
|
519
|
+
List[FeatureImportance]: Liste des importances de caractéristiques agrégées
|
|
520
|
+
"""
|
|
521
|
+
# Initialiser un dictionnaire pour stocker les importances agrégées
|
|
522
|
+
aggregated_importances = {name: [] for name in feature_names}
|
|
523
|
+
|
|
524
|
+
# Collecter toutes les importances
|
|
525
|
+
for exp in explanations:
|
|
526
|
+
# Pour les explainers tabulaires
|
|
527
|
+
if hasattr(exp, 'as_list'):
|
|
528
|
+
# Prendre la première classe si aucune n'est spécifiée
|
|
529
|
+
label = 1 if self._is_classifier() and exp.available_labels() else exp.available_labels()[0]
|
|
530
|
+
for feature, importance in exp.as_list(label=label):
|
|
531
|
+
# Extraire le nom de la caractéristique (peut contenir des conditions pour les valeurs catégorielles)
|
|
532
|
+
feature_name = feature.split(' ')[0]
|
|
533
|
+
if feature_name in aggregated_importances:
|
|
534
|
+
aggregated_importances[feature_name].append(abs(importance))
|
|
535
|
+
# Pour les explainers texte et image (structure différente)
|
|
536
|
+
elif hasattr(exp, 'local_exp'):
|
|
537
|
+
# Prendre la première classe
|
|
538
|
+
label = list(exp.local_exp.keys())[0]
|
|
539
|
+
for idx, importance in exp.local_exp[label]:
|
|
540
|
+
if idx < len(feature_names):
|
|
541
|
+
aggregated_importances[feature_names[idx]].append(abs(importance))
|
|
542
|
+
|
|
543
|
+
# Calculer les moyennes et créer les objets FeatureImportance
|
|
544
|
+
feature_importances = []
|
|
545
|
+
for name, importances in aggregated_importances.items():
|
|
546
|
+
if importances: # S'assurer qu'il y a des valeurs
|
|
547
|
+
avg_importance = np.mean(importances)
|
|
548
|
+
std_dev = np.std(importances) if len(importances) > 1 else 0.0
|
|
549
|
+
feature_importances.append(FeatureImportance(
|
|
550
|
+
feature_name=name,
|
|
551
|
+
importance=float(avg_importance),
|
|
552
|
+
std_dev=float(std_dev)
|
|
553
|
+
))
|
|
554
|
+
|
|
555
|
+
# Trier par importance décroissante
|
|
556
|
+
feature_importances.sort(key=lambda x: x.importance, reverse=True)
|
|
557
|
+
|
|
558
|
+
return feature_importances
|
|
559
|
+
|
|
560
|
+
def explain_instance(self, instance, **kwargs) -> ExplanationResult:
|
|
561
|
+
"""
|
|
562
|
+
Explique une instance spécifique avec LIME avec fonctionnalités avancées.
|
|
563
|
+
|
|
564
|
+
Cette implémentation avancée inclut:
|
|
565
|
+
- Support GPU optimisé pour les calculs complexes
|
|
566
|
+
- Système de cache intelligent pour les explications récurrentes
|
|
567
|
+
- Métriques de qualité et de fidélité des explications
|
|
568
|
+
- Génération de narratives explicatives multi-audience et multilingues
|
|
569
|
+
- Validation de conformité réglementaire intégrée
|
|
570
|
+
|
|
571
|
+
Args:
|
|
572
|
+
instance: Instance à expliquer (array, liste, dict ou pandas.Series)
|
|
573
|
+
**kwargs: Paramètres additionnels
|
|
574
|
+
- num_features: Nombre de caractéristiques à inclure dans l'explication
|
|
575
|
+
- num_samples: Nombre d'échantillons à générer pour l'approximation locale
|
|
576
|
+
- audience_level: Niveau d'audience (TECHNICAL, BUSINESS, PUBLIC)
|
|
577
|
+
- label: Indice de la classe à expliquer (pour les classifieurs multi-classes)
|
|
578
|
+
- use_cache: Activer/désactiver l'utilisation du cache (défaut: True)
|
|
579
|
+
- use_gpu: Utiliser GPU si disponible (défaut: configuration globale)
|
|
580
|
+
- compute_metrics: Calculer les métriques de qualité d'explication (défaut: True)
|
|
581
|
+
- include_prediction: Inclure la prédiction du modèle dans le résultat (défaut: True)
|
|
582
|
+
- generate_narrative: Générer des narratives textuelles explicatives (défaut: True)
|
|
583
|
+
- narrative_language: Langue des narratives ('fr', 'en') (défaut: 'fr')
|
|
584
|
+
- check_compliance: Vérifier la conformité réglementaire de l'explication (défaut: True)
|
|
585
|
+
- compliance_regulations: Liste des réglementations à vérifier (défaut: ['gdpr', 'ai_act'])
|
|
586
|
+
|
|
587
|
+
Returns:
|
|
588
|
+
ExplanationResult: Résultat standardisé de l'explication avec métriques avancées, narratives et conformité
|
|
589
|
+
"""
|
|
590
|
+
import hashlib
|
|
591
|
+
import pickle
|
|
592
|
+
import time
|
|
593
|
+
import sys
|
|
594
|
+
from dataclasses import dataclass, field
|
|
595
|
+
from contextlib import nullcontext
|
|
596
|
+
from datetime import datetime
|
|
597
|
+
|
|
598
|
+
# Import des modules internes pour mesurer la performance
|
|
599
|
+
try:
|
|
600
|
+
from ..core.profiling import Timer, MemoryTracker
|
|
601
|
+
except ImportError:
|
|
602
|
+
# Fallback simple si les modules ne sont pas disponibles
|
|
603
|
+
class Timer:
|
|
604
|
+
def __init__(self, name="Timer"):
|
|
605
|
+
self.name = name
|
|
606
|
+
self.start_time = None
|
|
607
|
+
def __enter__(self):
|
|
608
|
+
self.start_time = time.time()
|
|
609
|
+
return self
|
|
610
|
+
def __exit__(self, *args):
|
|
611
|
+
self.elapsed = time.time() - self.start_time
|
|
612
|
+
|
|
613
|
+
class MemoryTracker:
|
|
614
|
+
def __init__(self, name="MemoryTracker"):
|
|
615
|
+
self.name = name
|
|
616
|
+
self.start_mem = None
|
|
617
|
+
def __enter__(self):
|
|
618
|
+
self.start_mem = sys.getsizeof({})
|
|
619
|
+
return self
|
|
620
|
+
def __exit__(self, *args):
|
|
621
|
+
self.usage = 0 # Placeholder
|
|
622
|
+
|
|
623
|
+
# Import du vérificateur de conformité s'il est disponible
|
|
624
|
+
try:
|
|
625
|
+
from ..compliance.compliance_checker import ComplianceChecker
|
|
626
|
+
has_compliance_checker = True
|
|
627
|
+
except ImportError:
|
|
628
|
+
has_compliance_checker = False
|
|
629
|
+
|
|
630
|
+
# Configuration et paramètres avancés
|
|
631
|
+
@dataclass
|
|
632
|
+
class ExplainConfig:
|
|
633
|
+
# Paramètres basiques de LIME
|
|
634
|
+
num_features: int = 10
|
|
635
|
+
num_samples: int = 5000
|
|
636
|
+
audience_level: AudienceLevel = AudienceLevel.TECHNICAL
|
|
637
|
+
label: Optional[int] = None
|
|
638
|
+
feature_names: List[str] = field(default_factory=list)
|
|
639
|
+
|
|
640
|
+
# Paramètres avancés
|
|
641
|
+
use_cache: bool = True
|
|
642
|
+
use_gpu: bool = True # Utiliser GPU si disponible
|
|
643
|
+
compute_metrics: bool = True # Calculer les métriques de qualité
|
|
644
|
+
include_prediction: bool = True # Inclure la prédiction dans le résultat
|
|
645
|
+
generate_narrative: bool = True # Générer des narratives explicatives
|
|
646
|
+
narrative_language: str = 'fr' # Langue des narratives
|
|
647
|
+
check_compliance: bool = True # Vérifier la conformité réglementaire
|
|
648
|
+
compliance_regulations: List[str] = field(default_factory=lambda: ['gdpr', 'ai_act'])
|
|
649
|
+
|
|
650
|
+
# Créer et remplir la configuration
|
|
651
|
+
config = ExplainConfig(
|
|
652
|
+
num_features=kwargs.get('num_features', 10),
|
|
653
|
+
num_samples=kwargs.get('num_samples', 5000),
|
|
654
|
+
audience_level=kwargs.get('audience_level', AudienceLevel.TECHNICAL),
|
|
655
|
+
label=kwargs.get('label', None),
|
|
656
|
+
use_cache=kwargs.get('use_cache', True),
|
|
657
|
+
use_gpu=kwargs.get('use_gpu', getattr(self, '_config', {}).get('use_gpu', True)),
|
|
658
|
+
compute_metrics=kwargs.get('compute_metrics', True),
|
|
659
|
+
include_prediction=kwargs.get('include_prediction', True),
|
|
660
|
+
generate_narrative=kwargs.get('generate_narrative', True),
|
|
661
|
+
narrative_language=kwargs.get('narrative_language', 'fr'),
|
|
662
|
+
check_compliance=kwargs.get('check_compliance', True),
|
|
663
|
+
compliance_regulations=kwargs.get('compliance_regulations', ['gdpr', 'ai_act'])
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
# Initialiser trackers de performance
|
|
667
|
+
timer = Timer("LimeExplanation")
|
|
668
|
+
memory_tracker = MemoryTracker("LimeMemory")
|
|
669
|
+
|
|
670
|
+
with timer, memory_tracker:
|
|
671
|
+
# Convertir l'instance en format approprié
|
|
672
|
+
if isinstance(instance, dict):
|
|
673
|
+
# Convertir dict en array
|
|
674
|
+
feature_names = list(instance.keys())
|
|
675
|
+
instance_array = np.array([instance[f] for f in feature_names]).reshape(1, -1)
|
|
676
|
+
elif isinstance(instance, pd.Series):
|
|
677
|
+
feature_names = instance.index.tolist()
|
|
678
|
+
instance_array = instance.values.reshape(1, -1)
|
|
679
|
+
elif isinstance(instance, (list, np.ndarray)):
|
|
680
|
+
instance_array = np.array(instance).reshape(1, -1)
|
|
681
|
+
feature_names = kwargs.get('feature_names', self._feature_names) or \
|
|
682
|
+
[f"feature_{i}" for i in range(instance_array.shape[1])]
|
|
683
|
+
else:
|
|
684
|
+
raise ValueError("Format d'instance non supporté. Utilisez un dict, pandas.Series, liste ou numpy.ndarray.")
|
|
685
|
+
|
|
686
|
+
config.feature_names = feature_names
|
|
687
|
+
|
|
688
|
+
# Vérification du cache si activé
|
|
689
|
+
if config.use_cache and hasattr(self, '_explanation_cache'):
|
|
690
|
+
# Générer une clé de cache unique basée sur l'instance et les paramètres
|
|
691
|
+
cache_key_data = {
|
|
692
|
+
'instance': instance_array.tobytes(),
|
|
693
|
+
'num_features': config.num_features,
|
|
694
|
+
'num_samples': config.num_samples,
|
|
695
|
+
'label': config.label
|
|
696
|
+
}
|
|
697
|
+
cache_key = hashlib.md5(pickle.dumps(cache_key_data)).hexdigest()
|
|
698
|
+
|
|
699
|
+
# Vérifier si l'explication est dans le cache
|
|
700
|
+
if hasattr(self, '_explanation_cache') and cache_key in self._explanation_cache:
|
|
701
|
+
self._logger.info(f"Utilisation du cache pour l'explication (clé: {cache_key[:8]}...)")
|
|
702
|
+
return self._explanation_cache[cache_key]
|
|
703
|
+
else:
|
|
704
|
+
cache_key = None
|
|
705
|
+
|
|
706
|
+
# Si l'explainer n'est pas initialisé avec des données, le faire maintenant
|
|
707
|
+
if self._lime_explainer is None or (self._data_type == 'tabular' and
|
|
708
|
+
not hasattr(self._lime_explainer, 'feature_names')):
|
|
709
|
+
# Créer un petit ensemble de données synthétiques pour l'initialisation
|
|
710
|
+
synthetic_data = np.random.normal(0, 1, size=(100, instance_array.shape[1]))
|
|
711
|
+
self._initialize_explainer_with_data(synthetic_data, feature_names=feature_names)
|
|
712
|
+
|
|
713
|
+
# Tracer l'action
|
|
714
|
+
audit_data = {
|
|
715
|
+
"n_features": len(feature_names),
|
|
716
|
+
"audience_level": config.audience_level.value if isinstance(config.audience_level, AudienceLevel) else config.audience_level,
|
|
717
|
+
"num_features": config.num_features,
|
|
718
|
+
"num_samples": config.num_samples,
|
|
719
|
+
"use_gpu": config.use_gpu,
|
|
720
|
+
"use_cache": config.use_cache,
|
|
721
|
+
"timestamp": datetime.now().isoformat()
|
|
722
|
+
}
|
|
723
|
+
self.add_audit_record("explain_instance", audit_data)
|
|
724
|
+
|
|
725
|
+
# Context manager pour GPU si activé
|
|
726
|
+
gpu_context = self._maybe_use_gpu_context() if config.use_gpu else nullcontext()
|
|
727
|
+
|
|
728
|
+
# Prédiction si demandée
|
|
729
|
+
model_prediction = None
|
|
730
|
+
if config.include_prediction:
|
|
731
|
+
with gpu_context:
|
|
732
|
+
try:
|
|
733
|
+
model_prediction = self._model_predict_wrapper(instance_array)
|
|
734
|
+
except Exception as e:
|
|
735
|
+
self._logger.warning(f"Erreur lors de la prédiction du modèle: {str(e)}")
|
|
736
|
+
|
|
737
|
+
try:
|
|
738
|
+
# Génération de l'explication LIME avec optimisation GPU si configurée
|
|
739
|
+
with gpu_context:
|
|
740
|
+
# Générer l'explication pour cette instance
|
|
741
|
+
if self._data_type == 'tabular':
|
|
742
|
+
exp = self._lime_explainer.explain_instance(
|
|
743
|
+
instance_array[0],
|
|
744
|
+
self._model_predict_wrapper,
|
|
745
|
+
num_features=config.num_features,
|
|
746
|
+
num_samples=config.num_samples,
|
|
747
|
+
labels=[config.label] if config.label is not None else None
|
|
748
|
+
)
|
|
749
|
+
elif self._data_type == 'text':
|
|
750
|
+
exp = self._lime_explainer.explain_instance(
|
|
751
|
+
instance_array[0] if isinstance(instance_array[0], str) else str(instance_array[0]),
|
|
752
|
+
self._model_predict_wrapper,
|
|
753
|
+
num_features=config.num_features,
|
|
754
|
+
labels=[config.label] if config.label is not None else None
|
|
755
|
+
)
|
|
756
|
+
elif self._data_type == 'image':
|
|
757
|
+
exp = self._lime_explainer.explain_instance(
|
|
758
|
+
instance_array[0],
|
|
759
|
+
self._model_predict_wrapper,
|
|
760
|
+
num_features=config.num_features,
|
|
761
|
+
labels=[config.label] if config.label is not None else None
|
|
762
|
+
)
|
|
763
|
+
|
|
764
|
+
# Extraire les importances de caractéristiques
|
|
765
|
+
feature_importances = []
|
|
766
|
+
|
|
767
|
+
# Pour les explainers tabulaires
|
|
768
|
+
if hasattr(exp, 'as_list'):
|
|
769
|
+
# Déterminer la classe à expliquer
|
|
770
|
+
if config.label is None:
|
|
771
|
+
# Prendre la classe avec la probabilité maximale
|
|
772
|
+
predictions = self._model_predict_wrapper(instance_array)
|
|
773
|
+
if predictions.ndim > 1 and predictions.shape[1] > 1:
|
|
774
|
+
config.label = np.argmax(predictions[0])
|
|
775
|
+
else:
|
|
776
|
+
# Cas binaire, prendre la classe positive
|
|
777
|
+
config.label = 1 if self._is_classifier() else 0
|
|
778
|
+
|
|
779
|
+
# Extraire les importances
|
|
780
|
+
for feature, importance in exp.as_list(label=config.label):
|
|
781
|
+
# Extraire le nom de la caractéristique (peut contenir des conditions pour les valeurs catégorielles)
|
|
782
|
+
feature_name = feature.split(' ')[0]
|
|
783
|
+
feature_importances.append(FeatureImportance(
|
|
784
|
+
feature_name=feature_name,
|
|
785
|
+
importance=float(importance)
|
|
786
|
+
))
|
|
787
|
+
# Pour les explainers texte et image (structure différente)
|
|
788
|
+
elif hasattr(exp, 'local_exp'):
|
|
789
|
+
# Déterminer la classe à expliquer
|
|
790
|
+
if config.label is None:
|
|
791
|
+
# Prendre la première classe disponible
|
|
792
|
+
config.label = list(exp.local_exp.keys())[0]
|
|
793
|
+
|
|
794
|
+
# Extraire les importances
|
|
795
|
+
for idx, importance in exp.local_exp[config.label]:
|
|
796
|
+
if idx < len(feature_names):
|
|
797
|
+
feature_importances.append(FeatureImportance(
|
|
798
|
+
feature_name=feature_names[idx],
|
|
799
|
+
importance=float(importance)
|
|
800
|
+
))
|
|
801
|
+
|
|
802
|
+
# Trier par importance absolue décroissante
|
|
803
|
+
feature_importances.sort(key=lambda x: abs(x.importance), reverse=True)
|
|
804
|
+
|
|
805
|
+
# Métriques de qualité si demandé
|
|
806
|
+
explanation_metrics = {}
|
|
807
|
+
if config.compute_metrics:
|
|
808
|
+
explanation_metrics = self._compute_explanation_quality(feature_importances, exp, instance_array, config)
|
|
809
|
+
|
|
810
|
+
# Narratives si demandé
|
|
811
|
+
narratives = {}
|
|
812
|
+
if config.generate_narrative:
|
|
813
|
+
narratives = self._generate_explanation_narrative(
|
|
814
|
+
exp, instance_array, feature_names,
|
|
815
|
+
audience_level=config.audience_level,
|
|
816
|
+
language=config.narrative_language
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
# Extraire les métadonnées du modèle si nécessaire
|
|
820
|
+
if not self._metadata:
|
|
821
|
+
self._extract_metadata()
|
|
822
|
+
|
|
823
|
+
# Vérification de conformité si demandée
|
|
824
|
+
compliance_result = None
|
|
825
|
+
if config.check_compliance and has_compliance_checker:
|
|
826
|
+
try:
|
|
827
|
+
compliance_checker = ComplianceChecker()
|
|
828
|
+
# Créer un résultat d'explication temporaire pour la vérification
|
|
829
|
+
temp_result = ExplanationResult(
|
|
830
|
+
method=ExplainabilityMethod.LIME,
|
|
831
|
+
model_metadata=self._metadata,
|
|
832
|
+
feature_importances=feature_importances
|
|
833
|
+
)
|
|
834
|
+
compliance_result = compliance_checker.check_compliance(
|
|
835
|
+
temp_result,
|
|
836
|
+
regulations=config.compliance_regulations
|
|
837
|
+
)
|
|
838
|
+
except Exception as e:
|
|
839
|
+
self._logger.warning(f"Erreur lors de la vérification de conformité: {str(e)}")
|
|
840
|
+
|
|
841
|
+
# Enrichir les métadonnées avec les informations de performance
|
|
842
|
+
performance_metadata = {
|
|
843
|
+
"computation_time_ms": timer.elapsed * 1000 if hasattr(timer, "elapsed") else None,
|
|
844
|
+
"memory_usage_bytes": memory_tracker.usage if hasattr(memory_tracker, "usage") else None,
|
|
845
|
+
"computation_mode": "gpu" if config.use_gpu else "cpu",
|
|
846
|
+
"lime_sample_count": config.num_samples,
|
|
847
|
+
"timestamp": datetime.now().isoformat(),
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
# Créer le résultat d'explication enrichi
|
|
851
|
+
result = ExplanationResult(
|
|
852
|
+
method=ExplainabilityMethod.LIME,
|
|
853
|
+
model_metadata=self._metadata,
|
|
854
|
+
feature_importances=feature_importances,
|
|
855
|
+
raw_explanation={
|
|
856
|
+
"lime_explanation": exp,
|
|
857
|
+
"feature_names": feature_names,
|
|
858
|
+
"data": instance_array,
|
|
859
|
+
"label": config.label,
|
|
860
|
+
"prediction": model_prediction.tolist() if model_prediction is not None else None
|
|
861
|
+
},
|
|
862
|
+
audience_level=config.audience_level,
|
|
863
|
+
metrics=explanation_metrics,
|
|
864
|
+
narratives=narratives,
|
|
865
|
+
compliance=compliance_result.to_dict() if compliance_result else None,
|
|
866
|
+
metadata=performance_metadata
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
# Mettre en cache si activé
|
|
870
|
+
if config.use_cache and cache_key:
|
|
871
|
+
if not hasattr(self, '_explanation_cache'):
|
|
872
|
+
self._explanation_cache = {}
|
|
873
|
+
self._explanation_cache[cache_key] = result
|
|
874
|
+
# Limiter la taille du cache
|
|
875
|
+
if len(self._explanation_cache) > 100:
|
|
876
|
+
oldest_key = next(iter(self._explanation_cache))
|
|
877
|
+
del self._explanation_cache[oldest_key]
|
|
878
|
+
|
|
879
|
+
return result
|
|
880
|
+
except Exception as e:
|
|
881
|
+
self._logger.error(f"Erreur lors du calcul de l'explication LIME pour l'instance: {str(e)}")
|
|
882
|
+
import traceback
|
|
883
|
+
self._logger.debug(traceback.format_exc())
|
|
884
|
+
raise RuntimeError(f"Échec de l'explication LIME pour l'instance: {str(e)}")
|
|
885
|
+
|
|
886
|
+
def _maybe_use_gpu_context(self):
|
|
887
|
+
"""
|
|
888
|
+
Contexte de gestion des ressources GPU pour l'explication.
|
|
889
|
+
|
|
890
|
+
Permet d'optimiser automatiquement l'utilisation des ressources GPU
|
|
891
|
+
lors du calcul des valeurs LIME, particulièrement pour les grands jeux de données
|
|
892
|
+
et les modèles complexes.
|
|
893
|
+
|
|
894
|
+
Returns:
|
|
895
|
+
Un gestionnaire de contexte qui configure l'environnement GPU optimalement
|
|
896
|
+
"""
|
|
897
|
+
if not getattr(self, '_config', {}).get('use_gpu', False):
|
|
898
|
+
# Si GPU non activé, retourner un contexte vide (no-op)
|
|
899
|
+
from contextlib import nullcontext
|
|
900
|
+
return nullcontext()
|
|
901
|
+
|
|
902
|
+
# Contexte GPU adapté au framework détecté
|
|
903
|
+
model_type = self._extract_model_type()
|
|
904
|
+
|
|
905
|
+
class _GPUContext:
|
|
906
|
+
def __init__(self, explainer):
|
|
907
|
+
self._explainer = explainer
|
|
908
|
+
self._prev_state = None
|
|
909
|
+
self._tf_context = None
|
|
910
|
+
self._torch_context = None
|
|
911
|
+
|
|
912
|
+
def __enter__(self):
|
|
913
|
+
try:
|
|
914
|
+
if 'tensorflow' in model_type:
|
|
915
|
+
import tensorflow as tf
|
|
916
|
+
# Activer GPU pour TensorFlow
|
|
917
|
+
self._explainer._set_gpu_memory_growth()
|
|
918
|
+
# Enregistrer les devices disponibles pour les restaurer ensuite
|
|
919
|
+
self._prev_state = tf.config.get_visible_devices()
|
|
920
|
+
# N'autoriser que GPU si disponible
|
|
921
|
+
gpus = tf.config.list_physical_devices('GPU')
|
|
922
|
+
if gpus:
|
|
923
|
+
tf.config.set_visible_devices(gpus, 'GPU')
|
|
924
|
+
self._explainer._logger.info(f"GPU activé pour TensorFlow: {len(gpus)} périphériques")
|
|
925
|
+
else:
|
|
926
|
+
self._explainer._logger.warning("GPU demandé mais non disponible pour TensorFlow")
|
|
927
|
+
|
|
928
|
+
elif 'pytorch' in model_type or 'torch' in model_type:
|
|
929
|
+
import torch
|
|
930
|
+
# Activer CUDA pour PyTorch si disponible
|
|
931
|
+
if torch.cuda.is_available():
|
|
932
|
+
# Préparer le device et définir le contexte par défaut
|
|
933
|
+
self._prev_state = torch.cuda.current_device()
|
|
934
|
+
torch.cuda.set_device(0) # Premier GPU par défaut
|
|
935
|
+
self._explainer._logger.info(f"GPU activé pour PyTorch: {torch.cuda.get_device_name(0)}")
|
|
936
|
+
else:
|
|
937
|
+
self._explainer._logger.warning("GPU demandé mais CUDA non disponible pour PyTorch")
|
|
938
|
+
|
|
939
|
+
# Configuration spécifique pour LIME
|
|
940
|
+
# LIME n'a pas d'API publique pour configurer le GPU, mais certaines
|
|
941
|
+
# opérations comme les calculs de similarité peuvent bénéficier du GPU
|
|
942
|
+
# si le framework sous-jacent est configuré correctement
|
|
943
|
+
except Exception as e:
|
|
944
|
+
self._explainer._logger.error(f"Erreur lors de l'activation GPU: {str(e)}")
|
|
945
|
+
|
|
946
|
+
return self
|
|
947
|
+
|
|
948
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
949
|
+
try:
|
|
950
|
+
# Restaurer l'état précédent
|
|
951
|
+
if 'tensorflow' in model_type and self._prev_state is not None:
|
|
952
|
+
import tensorflow as tf
|
|
953
|
+
tf.config.set_visible_devices(self._prev_state)
|
|
954
|
+
elif ('pytorch' in model_type or 'torch' in model_type) and self._prev_state is not None:
|
|
955
|
+
import torch
|
|
956
|
+
if torch.cuda.is_available():
|
|
957
|
+
torch.cuda.set_device(self._prev_state)
|
|
958
|
+
# Libérer la mémoire GPU
|
|
959
|
+
torch.cuda.empty_cache()
|
|
960
|
+
except Exception as e:
|
|
961
|
+
self._explainer._logger.error(f"Erreur lors de la restauration du contexte GPU: {str(e)}")
|
|
962
|
+
|
|
963
|
+
# Retourner une instance du gestionnaire de contexte
|
|
964
|
+
return _GPUContext(self)
|
|
965
|
+
|
|
966
|
+
def _set_gpu_memory_growth(self):
|
|
967
|
+
"""
|
|
968
|
+
Configure la croissance mémoire GPU dynamique pour TensorFlow.
|
|
969
|
+
|
|
970
|
+
Cette méthode contextuelle permet une utilisation optimale de la mémoire GPU
|
|
971
|
+
en configurant une allocation dynamique, évitant ainsi les erreurs OOM et
|
|
972
|
+
permettant une meilleure répartition des ressources entre plusieurs processus.
|
|
973
|
+
"""
|
|
974
|
+
try:
|
|
975
|
+
import tensorflow as tf
|
|
976
|
+
gpus = tf.config.list_physical_devices('GPU')
|
|
977
|
+
if gpus:
|
|
978
|
+
# Configurer la croissance mémoire dynamique sur tous les GPU
|
|
979
|
+
for gpu in gpus:
|
|
980
|
+
tf.config.experimental.set_memory_growth(gpu, True)
|
|
981
|
+
self._logger.info(f"Allocation mémoire GPU dynamique activée pour {len(gpus)} GPU(s)")
|
|
982
|
+
except ImportError:
|
|
983
|
+
self._logger.info("TensorFlow non installé, croissance mémoire GPU non configurée")
|
|
984
|
+
except Exception as e:
|
|
985
|
+
self._logger.warning(f"Erreur lors de la configuration de la mémoire GPU: {str(e)}")
|
|
986
|
+
|
|
987
|
+
def _extract_model_type(self):
|
|
988
|
+
"""
|
|
989
|
+
Détermine le type/framework du modèle pour adapter les optimisations.
|
|
990
|
+
|
|
991
|
+
Returns:
|
|
992
|
+
str: Type de modèle détecté ('tensorflow', 'pytorch', etc.)
|
|
993
|
+
"""
|
|
994
|
+
model_module = self._model.__class__.__module__
|
|
995
|
+
|
|
996
|
+
if 'sklearn' in model_module:
|
|
997
|
+
return 'sklearn'
|
|
998
|
+
elif 'xgboost' in model_module:
|
|
999
|
+
return 'xgboost'
|
|
1000
|
+
elif 'lightgbm' in model_module:
|
|
1001
|
+
return 'lightgbm'
|
|
1002
|
+
elif 'catboost' in model_module:
|
|
1003
|
+
return 'catboost'
|
|
1004
|
+
elif 'tensorflow' in model_module or 'keras' in model_module:
|
|
1005
|
+
return 'tensorflow'
|
|
1006
|
+
elif 'torch' in model_module:
|
|
1007
|
+
return 'pytorch'
|
|
1008
|
+
else:
|
|
1009
|
+
return 'unknown'
|
|
1010
|
+
|
|
1011
|
+
def _extract_metadata(self) -> None:
|
|
1012
|
+
"""
|
|
1013
|
+
Extrait les métadonnées du modèle pour l'explication.
|
|
1014
|
+
"""
|
|
1015
|
+
model_module = self._model.__class__.__module__
|
|
1016
|
+
framework = None
|
|
1017
|
+
|
|
1018
|
+
# Déterminer le framework
|
|
1019
|
+
if 'sklearn' in model_module:
|
|
1020
|
+
framework = 'sklearn'
|
|
1021
|
+
elif 'xgboost' in model_module:
|
|
1022
|
+
framework = 'xgboost'
|
|
1023
|
+
elif 'lightgbm' in model_module:
|
|
1024
|
+
framework = 'lightgbm'
|
|
1025
|
+
elif 'catboost' in model_module:
|
|
1026
|
+
framework = 'catboost'
|
|
1027
|
+
elif 'tensorflow' in model_module or 'keras' in model_module:
|
|
1028
|
+
framework = 'tensorflow'
|
|
1029
|
+
elif 'torch' in model_module:
|
|
1030
|
+
framework = 'pytorch'
|
|
1031
|
+
else:
|
|
1032
|
+
framework = 'unknown'
|
|
1033
|
+
|
|
1034
|
+
# Déterminer le type de modèle (classification ou régression)
|
|
1035
|
+
is_classifier = self._is_classifier()
|
|
1036
|
+
|
|
1037
|
+
# Créer les métadonnées
|
|
1038
|
+
self._metadata = ModelMetadata(
|
|
1039
|
+
model_type="classification" if is_classifier else "regression",
|
|
1040
|
+
framework=framework,
|
|
1041
|
+
input_shape=self._infer_model_input_shape(),
|
|
1042
|
+
output_shape=None, # À compléter si nécessaire
|
|
1043
|
+
feature_names=self._feature_names,
|
|
1044
|
+
target_names=self._class_names,
|
|
1045
|
+
model_params={},
|
|
1046
|
+
model_version="1.0.0"
|
|
1047
|
+
)
|
|
1048
|
+
|
|
1049
|
+
def _compute_explanation_quality(self, feature_importances, lime_explanation, instance, config):
|
|
1050
|
+
"""
|
|
1051
|
+
Calcule les métriques de qualité pour l'explication LIME.
|
|
1052
|
+
|
|
1053
|
+
Args:
|
|
1054
|
+
feature_importances: Liste des importances des caractéristiques calculées
|
|
1055
|
+
lime_explanation: Objet d'explication LIME brut
|
|
1056
|
+
instance: Instance à expliquer
|
|
1057
|
+
config: Configuration de l'explication
|
|
1058
|
+
|
|
1059
|
+
Returns:
|
|
1060
|
+
dict: Métriques de qualité de l'explication
|
|
1061
|
+
"""
|
|
1062
|
+
metrics = {}
|
|
1063
|
+
|
|
1064
|
+
try:
|
|
1065
|
+
# 1. Fidélité locale - mesure à quel point l'explication
|
|
1066
|
+
# prédit correctement le comportement du modèle dans le voisinage
|
|
1067
|
+
if hasattr(lime_explanation, 'score') and lime_explanation.score is not None:
|
|
1068
|
+
metrics['local_fidelity'] = float(lime_explanation.score)
|
|
1069
|
+
elif hasattr(lime_explanation, 'local_pred') and hasattr(lime_explanation, 'score'):
|
|
1070
|
+
metrics['local_fidelity'] = float(lime_explanation.score)
|
|
1071
|
+
else:
|
|
1072
|
+
# Calculer la fidélité manuellement si LIME ne la fournit pas
|
|
1073
|
+
if hasattr(lime_explanation, 'intercept') and hasattr(lime_explanation, 'local_exp'):
|
|
1074
|
+
try:
|
|
1075
|
+
# Récupérer les données du modèle linéaire simplifié de LIME
|
|
1076
|
+
label = config.label if config.label is not None else 0
|
|
1077
|
+
coefs = dict(lime_explanation.local_exp[label])
|
|
1078
|
+
intercept = lime_explanation.intercept[label] if isinstance(lime_explanation.intercept, dict) else lime_explanation.intercept
|
|
1079
|
+
|
|
1080
|
+
# Obtenir les vraies prédictions du modèle
|
|
1081
|
+
true_pred = self._model_predict_wrapper(instance)[0]
|
|
1082
|
+
|
|
1083
|
+
# Calculer l'écart entre le modèle LIME et le vrai modèle
|
|
1084
|
+
if self._is_classifier():
|
|
1085
|
+
# Classification: utiliser l'erreur moyenne absolue sur les probabilités
|
|
1086
|
+
metrics['local_fidelity'] = 1 - np.mean(np.abs(lime_explanation.predict_proba - true_pred))
|
|
1087
|
+
else:
|
|
1088
|
+
# Régression: utiliser l'erreur absolue normalisée
|
|
1089
|
+
pred_abs_error = np.abs(lime_explanation.predict(instance.reshape(1, -1))[0] - true_pred[0])
|
|
1090
|
+
metrics['local_fidelity'] = 1 - min(pred_abs_error / (np.abs(true_pred[0]) + 1e-10), 1.0)
|
|
1091
|
+
except Exception as e:
|
|
1092
|
+
self._logger.warning(f"Erreur lors du calcul de la fidélité locale: {str(e)}")
|
|
1093
|
+
metrics['local_fidelity'] = None
|
|
1094
|
+
|
|
1095
|
+
# 2. Impact de prédiction - somme des valeurs absolues des importances
|
|
1096
|
+
total_abs_importance = sum(abs(fi.importance) for fi in feature_importances)
|
|
1097
|
+
metrics['prediction_impact'] = float(total_abs_importance)
|
|
1098
|
+
|
|
1099
|
+
# 3. Indice de Gini pour mesurer la concentration des importances
|
|
1100
|
+
# Valeur proche de 1 = explication concentrée sur peu de features
|
|
1101
|
+
# Valeur proche de 0 = explication distribuée uniformément
|
|
1102
|
+
if feature_importances:
|
|
1103
|
+
metrics['feature_concentration'] = self._gini_index(
|
|
1104
|
+
[abs(fi.importance) for fi in feature_importances]
|
|
1105
|
+
)
|
|
1106
|
+
else:
|
|
1107
|
+
metrics['feature_concentration'] = 0.0
|
|
1108
|
+
|
|
1109
|
+
# 4. Stabilité de l'explication (si explicitement demandée)
|
|
1110
|
+
# Cette métrique est coûteuse car elle nécessite de multiples explications
|
|
1111
|
+
if config.compute_stability and hasattr(instance, 'shape'):
|
|
1112
|
+
try:
|
|
1113
|
+
# Créer des perturbations légères de l'instance
|
|
1114
|
+
stability_samples = 5
|
|
1115
|
+
eps = 1e-2
|
|
1116
|
+
perturbed_instances = []
|
|
1117
|
+
|
|
1118
|
+
# Générer des instances avec du bruit gaussien
|
|
1119
|
+
rng = np.random.RandomState(42)
|
|
1120
|
+
for _ in range(stability_samples):
|
|
1121
|
+
# Ajouter un bruit gaussien faible aux instances
|
|
1122
|
+
perturbed = instance.copy()
|
|
1123
|
+
noise = eps * rng.randn(*perturbed.shape)
|
|
1124
|
+
perturbed += noise
|
|
1125
|
+
perturbed_instances.append(perturbed)
|
|
1126
|
+
|
|
1127
|
+
# Générer des explications pour les instances perturbées
|
|
1128
|
+
# Sans utiliser explain_instance pour éviter la récursion
|
|
1129
|
+
feature_imp_lists = []
|
|
1130
|
+
lime_explainer = self._get_lime_explainer()
|
|
1131
|
+
|
|
1132
|
+
for p_instance in perturbed_instances:
|
|
1133
|
+
p_exp = lime_explainer.explain_instance(
|
|
1134
|
+
p_instance[0],
|
|
1135
|
+
self._model.predict_proba if hasattr(self._model, 'predict_proba') else self._model.predict,
|
|
1136
|
+
num_features=len(feature_importances),
|
|
1137
|
+
num_samples=config.num_samples
|
|
1138
|
+
)
|
|
1139
|
+
# Extraire les importances pour la même classe
|
|
1140
|
+
if hasattr(p_exp, 'as_list'):
|
|
1141
|
+
imp_list = dict([(f.split(' ')[0], imp) for f, imp in p_exp.as_list(label=config.label)])
|
|
1142
|
+
elif hasattr(p_exp, 'local_exp'):
|
|
1143
|
+
imp_list = dict(p_exp.local_exp[config.label])
|
|
1144
|
+
else:
|
|
1145
|
+
imp_list = {}
|
|
1146
|
+
feature_imp_lists.append(imp_list)
|
|
1147
|
+
|
|
1148
|
+
# Calculer la similarité entre les explications (corrélation moyenne des importances)
|
|
1149
|
+
feature_names = [fi.feature_name for fi in feature_importances]
|
|
1150
|
+
stability_scores = []
|
|
1151
|
+
|
|
1152
|
+
for i in range(stability_samples):
|
|
1153
|
+
for j in range(i+1, stability_samples):
|
|
1154
|
+
imp_list_i = feature_imp_lists[i]
|
|
1155
|
+
imp_list_j = feature_imp_lists[j]
|
|
1156
|
+
|
|
1157
|
+
# Récupérer les importances pour les mêmes features
|
|
1158
|
+
common_features = set(imp_list_i.keys()).intersection(imp_list_j.keys())
|
|
1159
|
+
if common_features:
|
|
1160
|
+
vec_i = [imp_list_i.get(f, 0) for f in common_features]
|
|
1161
|
+
vec_j = [imp_list_j.get(f, 0) for f in common_features]
|
|
1162
|
+
|
|
1163
|
+
# Calculer la corrélation comme mesure de similarité
|
|
1164
|
+
from scipy.stats import spearmanr
|
|
1165
|
+
corr, _ = spearmanr(vec_i, vec_j)
|
|
1166
|
+
stability_scores.append(corr if not np.isnan(corr) else 0)
|
|
1167
|
+
|
|
1168
|
+
if stability_scores:
|
|
1169
|
+
# La stabilité est la corrélation moyenne entre les explications
|
|
1170
|
+
metrics['stability'] = float(np.mean(stability_scores))
|
|
1171
|
+
else:
|
|
1172
|
+
metrics['stability'] = None
|
|
1173
|
+
except Exception as e:
|
|
1174
|
+
self._logger.warning(f"Erreur lors du calcul de la stabilité de l'explication: {str(e)}")
|
|
1175
|
+
metrics['stability'] = None
|
|
1176
|
+
|
|
1177
|
+
# 5. Complexité de l'explication (nombre de features avec importance significative)
|
|
1178
|
+
importance_threshold = 0.01
|
|
1179
|
+
significant_features = sum(1 for fi in feature_importances if abs(fi.importance) > importance_threshold)
|
|
1180
|
+
metrics['complexity'] = significant_features
|
|
1181
|
+
|
|
1182
|
+
# 6. Déterminer les métriques spécifiques au type d'explication (tabular, text, image)
|
|
1183
|
+
if hasattr(lime_explanation, 'domain_mapper'):
|
|
1184
|
+
if hasattr(lime_explanation.domain_mapper, 'type'):
|
|
1185
|
+
metrics['explanation_type'] = lime_explanation.domain_mapper.type
|
|
1186
|
+
elif 'text' in str(lime_explanation.domain_mapper.__class__).lower():
|
|
1187
|
+
metrics['explanation_type'] = 'text'
|
|
1188
|
+
elif 'image' in str(lime_explanation.domain_mapper.__class__).lower():
|
|
1189
|
+
metrics['explanation_type'] = 'image'
|
|
1190
|
+
else:
|
|
1191
|
+
metrics['explanation_type'] = 'tabular'
|
|
1192
|
+
except Exception as e:
|
|
1193
|
+
self._logger.warning(f"Erreur lors du calcul des métriques de qualité: {str(e)}")
|
|
1194
|
+
import traceback
|
|
1195
|
+
self._logger.debug(traceback.format_exc())
|
|
1196
|
+
|
|
1197
|
+
return metrics
|
|
1198
|
+
|
|
1199
|
+
def _gini_index(self, values):
|
|
1200
|
+
"""
|
|
1201
|
+
Calcule l'indice de Gini pour mesurer la concentration/dispersion des importances.
|
|
1202
|
+
|
|
1203
|
+
L'indice de Gini est compris entre 0 (distribution égale) et 1 (concentration extrême).
|
|
1204
|
+
|
|
1205
|
+
Args:
|
|
1206
|
+
values: Liste des valeurs d'importance (absolues)
|
|
1207
|
+
|
|
1208
|
+
Returns:
|
|
1209
|
+
float: Indice de Gini entre 0 et 1
|
|
1210
|
+
"""
|
|
1211
|
+
# Filtrer les valeurs nulles ou négatives
|
|
1212
|
+
values = [v for v in values if v > 0]
|
|
1213
|
+
n = len(values)
|
|
1214
|
+
|
|
1215
|
+
if n <= 1:
|
|
1216
|
+
return 0.0
|
|
1217
|
+
|
|
1218
|
+
# Trier les valeurs
|
|
1219
|
+
values = sorted(values)
|
|
1220
|
+
|
|
1221
|
+
# Normaliser les valeurs (somme = 1)
|
|
1222
|
+
total = sum(values)
|
|
1223
|
+
if total == 0:
|
|
1224
|
+
return 0.0
|
|
1225
|
+
|
|
1226
|
+
values = [v / total for v in values]
|
|
1227
|
+
|
|
1228
|
+
# Calculer l'indice de Gini
|
|
1229
|
+
cumsum = 0
|
|
1230
|
+
gini = 0
|
|
1231
|
+
|
|
1232
|
+
for i, v in enumerate(values):
|
|
1233
|
+
rank = i + 1
|
|
1234
|
+
cumsum += v
|
|
1235
|
+
gini += v * ((n + 1 - rank) / n)
|
|
1236
|
+
|
|
1237
|
+
# Normaliser entre 0 et 1
|
|
1238
|
+
gini = 1 - 2 * gini
|
|
1239
|
+
|
|
1240
|
+
return max(0.0, min(1.0, gini)) # Assurer que Gini est entre 0 et 1
|
|
1241
|
+
|
|
1242
|
+
def _generate_explanation_narrative(self, lime_explanation, instance, feature_names, audience_level="technical", language="en"):
|
|
1243
|
+
"""
|
|
1244
|
+
Génère une explication narrative des résultats LIME adaptée au niveau d'audience.
|
|
1245
|
+
|
|
1246
|
+
Args:
|
|
1247
|
+
lime_explanation: L'objet d'explication LIME
|
|
1248
|
+
instance: L'instance expliquée
|
|
1249
|
+
feature_names: Noms des caractéristiques
|
|
1250
|
+
audience_level: Niveau d'audience cible ("technical", "business", "public")
|
|
1251
|
+
language: Langue de l'explication ("en", "fr")
|
|
1252
|
+
|
|
1253
|
+
Returns:
|
|
1254
|
+
dict: Narratives explicatives par niveau d'audience
|
|
1255
|
+
"""
|
|
1256
|
+
# Vérifier que l'objet d'explication est valide
|
|
1257
|
+
if lime_explanation is None:
|
|
1258
|
+
return {}
|
|
1259
|
+
|
|
1260
|
+
# Initialiser le dictionnaire de narratives
|
|
1261
|
+
narratives = {}
|
|
1262
|
+
|
|
1263
|
+
try:
|
|
1264
|
+
# Déterminer si nous sommes dans un cas de classification ou régression
|
|
1265
|
+
is_classifier = self._is_classifier()
|
|
1266
|
+
|
|
1267
|
+
# Récupérer les top N caractéristiques les plus importantes (en valeur absolue)
|
|
1268
|
+
top_n = 5
|
|
1269
|
+
important_features = []
|
|
1270
|
+
|
|
1271
|
+
if hasattr(lime_explanation, 'as_list'):
|
|
1272
|
+
label = getattr(lime_explanation, 'label', 0)
|
|
1273
|
+
features = lime_explanation.as_list(label=label)
|
|
1274
|
+
# Trier par importance absolue
|
|
1275
|
+
features.sort(key=lambda x: abs(x[1]), reverse=True)
|
|
1276
|
+
important_features = features[:top_n]
|
|
1277
|
+
elif hasattr(lime_explanation, 'local_exp'):
|
|
1278
|
+
# Pour les explainers texte/image
|
|
1279
|
+
label = list(lime_explanation.local_exp.keys())[0]
|
|
1280
|
+
features = [(feature_names[idx] if idx < len(feature_names) else f"feature_{idx}", imp)
|
|
1281
|
+
for idx, imp in lime_explanation.local_exp[label]]
|
|
1282
|
+
features.sort(key=lambda x: abs(x[1]), reverse=True)
|
|
1283
|
+
important_features = features[:top_n]
|
|
1284
|
+
|
|
1285
|
+
# Séparer les caractéristiques positives et négatives
|
|
1286
|
+
positive_features = [(name, importance) for name, importance in important_features if importance > 0]
|
|
1287
|
+
negative_features = [(name, importance) for name, importance in important_features if importance < 0]
|
|
1288
|
+
|
|
1289
|
+
# Générer la prédiction du modèle pour cette instance si disponible
|
|
1290
|
+
prediction = None
|
|
1291
|
+
pred_value = None
|
|
1292
|
+
confidence = None
|
|
1293
|
+
|
|
1294
|
+
try:
|
|
1295
|
+
# Obtenir la prédiction brute
|
|
1296
|
+
raw_prediction = self._model_predict_wrapper(instance)[0]
|
|
1297
|
+
|
|
1298
|
+
if is_classifier:
|
|
1299
|
+
if raw_prediction.ndim > 0 and len(raw_prediction) > 1:
|
|
1300
|
+
# Cas multi-classe
|
|
1301
|
+
pred_class = np.argmax(raw_prediction)
|
|
1302
|
+
confidence = float(raw_prediction[pred_class])
|
|
1303
|
+
|
|
1304
|
+
# Récupérer le nom de la classe si disponible
|
|
1305
|
+
if self._class_names and pred_class < len(self._class_names):
|
|
1306
|
+
prediction = self._class_names[pred_class]
|
|
1307
|
+
else:
|
|
1308
|
+
prediction = f"Classe {pred_class}"
|
|
1309
|
+
else:
|
|
1310
|
+
# Cas binaire
|
|
1311
|
+
pred_value = float(raw_prediction[0]) if raw_prediction.ndim > 0 else float(raw_prediction)
|
|
1312
|
+
threshold = 0.5
|
|
1313
|
+
pred_class = 1 if pred_value >= threshold else 0
|
|
1314
|
+
|
|
1315
|
+
if self._class_names and len(self._class_names) > 1:
|
|
1316
|
+
prediction = self._class_names[pred_class]
|
|
1317
|
+
else:
|
|
1318
|
+
prediction = "Positif" if pred_class == 1 else "Négatif"
|
|
1319
|
+
|
|
1320
|
+
confidence = pred_value if pred_class == 1 else 1 - pred_value
|
|
1321
|
+
else:
|
|
1322
|
+
# Cas régression
|
|
1323
|
+
pred_value = float(raw_prediction[0]) if raw_prediction.ndim > 0 else float(raw_prediction)
|
|
1324
|
+
prediction = f"{pred_value:.4f}"
|
|
1325
|
+
except Exception as e:
|
|
1326
|
+
self._logger.warning(f"Erreur lors de l'extraction de la prédiction: {str(e)}")
|
|
1327
|
+
|
|
1328
|
+
# Générer les narratives pour chaque niveau d'audience et selon la langue
|
|
1329
|
+
|
|
1330
|
+
# ===== NIVEAU TECHNIQUE =====
|
|
1331
|
+
if audience_level in ["technical", "all"]:
|
|
1332
|
+
technical_narrative = self._generate_technical_narrative(important_features, prediction, confidence,
|
|
1333
|
+
is_classifier, language)
|
|
1334
|
+
narratives["technical"] = technical_narrative
|
|
1335
|
+
|
|
1336
|
+
# ===== NIVEAU BUSINESS =====
|
|
1337
|
+
if audience_level in ["business", "all"]:
|
|
1338
|
+
business_narrative = self._generate_business_narrative(positive_features, negative_features,
|
|
1339
|
+
prediction, confidence, is_classifier, language)
|
|
1340
|
+
narratives["business"] = business_narrative
|
|
1341
|
+
|
|
1342
|
+
# ===== NIVEAU PUBLIC =====
|
|
1343
|
+
if audience_level in ["public", "all"]:
|
|
1344
|
+
public_narrative = self._generate_public_narrative(positive_features, negative_features,
|
|
1345
|
+
prediction, is_classifier, language)
|
|
1346
|
+
narratives["public"] = public_narrative
|
|
1347
|
+
|
|
1348
|
+
except Exception as e:
|
|
1349
|
+
self._logger.warning(f"Erreur lors de la génération de narrative: {str(e)}")
|
|
1350
|
+
import traceback
|
|
1351
|
+
self._logger.debug(traceback.format_exc())
|
|
1352
|
+
|
|
1353
|
+
# Fournir une narrative par défaut en cas d'erreur
|
|
1354
|
+
if language == "fr":
|
|
1355
|
+
narratives = {"technical": "Impossible de générer une explication narrative technique."}
|
|
1356
|
+
else:
|
|
1357
|
+
narratives = {"technical": "Unable to generate a technical narrative explanation."}
|
|
1358
|
+
|
|
1359
|
+
return narratives
|
|
1360
|
+
|
|
1361
|
+
def _generate_technical_narrative(self, important_features, prediction, confidence, is_classifier, language="en"):
|
|
1362
|
+
"""
|
|
1363
|
+
Génère une narrative technique détaillée basée sur les résultats LIME.
|
|
1364
|
+
|
|
1365
|
+
Args:
|
|
1366
|
+
important_features: Liste des caractéristiques importantes et leurs valeurs
|
|
1367
|
+
prediction: Prédiction du modèle (classe ou valeur)
|
|
1368
|
+
confidence: Confiance de la prédiction (pour la classification)
|
|
1369
|
+
is_classifier: Indique si c'est un classificateur ou régresseur
|
|
1370
|
+
language: Langue de l'explication ("en", "fr")
|
|
1371
|
+
|
|
1372
|
+
Returns:
|
|
1373
|
+
str: Narrative technique
|
|
1374
|
+
"""
|
|
1375
|
+
if language == "fr":
|
|
1376
|
+
# Version française
|
|
1377
|
+
if is_classifier:
|
|
1378
|
+
intro = f"Le modèle prédit la classe {prediction}"
|
|
1379
|
+
if confidence is not None:
|
|
1380
|
+
intro += f" avec une confiance de {confidence:.2%}."
|
|
1381
|
+
else:
|
|
1382
|
+
intro += "."
|
|
1383
|
+
else:
|
|
1384
|
+
intro = f"Le modèle prédit la valeur {prediction}."
|
|
1385
|
+
|
|
1386
|
+
# Détails des caractéristiques
|
|
1387
|
+
details = "D'après l'analyse LIME, les facteurs explicatifs les plus importants sont:\n"
|
|
1388
|
+
|
|
1389
|
+
for i, (name, importance) in enumerate(important_features):
|
|
1390
|
+
details += f"- {name}: impact de {importance:.4f} "
|
|
1391
|
+
if importance > 0:
|
|
1392
|
+
details += "(augmente la prédiction)"
|
|
1393
|
+
else:
|
|
1394
|
+
details += "(diminue la prédiction)"
|
|
1395
|
+
details += "\n"
|
|
1396
|
+
|
|
1397
|
+
# Ajouter des détails techniques
|
|
1398
|
+
conclusion = "\nL'algorithme LIME a généré un modèle local linéaire interprétable "
|
|
1399
|
+
conclusion += "qui approxime le comportement du modèle complexe dans le voisinage de cette instance. "
|
|
1400
|
+
conclusion += "Les coefficients représentent l'importance relative de chaque caractéristique dans la prédiction."
|
|
1401
|
+
|
|
1402
|
+
else:
|
|
1403
|
+
# Version anglaise (défaut)
|
|
1404
|
+
if is_classifier:
|
|
1405
|
+
intro = f"The model predicts class {prediction}"
|
|
1406
|
+
if confidence is not None:
|
|
1407
|
+
intro += f" with a confidence of {confidence:.2%}."
|
|
1408
|
+
else:
|
|
1409
|
+
intro += "."
|
|
1410
|
+
else:
|
|
1411
|
+
intro = f"The model predicts the value {prediction}."
|
|
1412
|
+
|
|
1413
|
+
# Feature details
|
|
1414
|
+
details = "According to LIME analysis, the most significant explanatory factors are:\n"
|
|
1415
|
+
|
|
1416
|
+
for i, (name, importance) in enumerate(important_features):
|
|
1417
|
+
details += f"- {name}: impact of {importance:.4f} "
|
|
1418
|
+
if importance > 0:
|
|
1419
|
+
details += "(increases the prediction)"
|
|
1420
|
+
else:
|
|
1421
|
+
details += "(decreases the prediction)"
|
|
1422
|
+
details += "\n"
|
|
1423
|
+
|
|
1424
|
+
# Add technical details
|
|
1425
|
+
conclusion = "\nThe LIME algorithm generated an interpretable linear local model "
|
|
1426
|
+
conclusion += "that approximates the behavior of the complex model in the vicinity of this instance. "
|
|
1427
|
+
conclusion += "The coefficients represent the relative importance of each feature in the prediction."
|
|
1428
|
+
|
|
1429
|
+
return intro + "\n\n" + details + conclusion
|
|
1430
|
+
|
|
1431
|
+
def _generate_business_narrative(self, positive_features, negative_features, prediction, confidence, is_classifier, language="en"):
|
|
1432
|
+
"""
|
|
1433
|
+
Génère une narrative orientée business basée sur les résultats LIME.
|
|
1434
|
+
|
|
1435
|
+
Args:
|
|
1436
|
+
positive_features: Caractéristiques avec impact positif
|
|
1437
|
+
negative_features: Caractéristiques avec impact négatif
|
|
1438
|
+
prediction: Prédiction du modèle (classe ou valeur)
|
|
1439
|
+
confidence: Confiance de la prédiction
|
|
1440
|
+
is_classifier: Indique si c'est un classificateur ou régresseur
|
|
1441
|
+
language: Langue de l'explication ("en", "fr")
|
|
1442
|
+
|
|
1443
|
+
Returns:
|
|
1444
|
+
str: Narrative business
|
|
1445
|
+
"""
|
|
1446
|
+
if language == "fr":
|
|
1447
|
+
# Version française
|
|
1448
|
+
if is_classifier:
|
|
1449
|
+
summary = f"Résultat: {prediction}"
|
|
1450
|
+
if confidence is not None:
|
|
1451
|
+
summary += f" (confiance: {confidence:.1%})\n\n"
|
|
1452
|
+
else:
|
|
1453
|
+
summary += "\n\n"
|
|
1454
|
+
|
|
1455
|
+
if confidence is not None and confidence < 0.7:
|
|
1456
|
+
summary += "Attention: La confiance du modèle dans cette prédiction est relativement faible.\n\n"
|
|
1457
|
+
else:
|
|
1458
|
+
summary = f"Résultat: La valeur prédite est {prediction}\n\n"
|
|
1459
|
+
|
|
1460
|
+
# Facteurs influents
|
|
1461
|
+
factors = "Principaux facteurs influents:\n"
|
|
1462
|
+
|
|
1463
|
+
if positive_features:
|
|
1464
|
+
factors += "\nFacteurs favorables:\n"
|
|
1465
|
+
for name, importance in positive_features:
|
|
1466
|
+
factors += f"- {name} (impact: +{abs(importance):.2f})\n"
|
|
1467
|
+
|
|
1468
|
+
if negative_features:
|
|
1469
|
+
factors += "\nFacteurs défavorables:\n"
|
|
1470
|
+
for name, importance in negative_features:
|
|
1471
|
+
factors += f"- {name} (impact: -{abs(importance):.2f})\n"
|
|
1472
|
+
|
|
1473
|
+
# Recommandations
|
|
1474
|
+
recommendations = "\nRecommandations:\n"
|
|
1475
|
+
if positive_features and not is_classifier:
|
|
1476
|
+
recommendations += "- Pour augmenter la prédiction, concentrez-vous sur l'amélioration des facteurs favorables.\n"
|
|
1477
|
+
elif negative_features and not is_classifier:
|
|
1478
|
+
recommendations += "- Pour diminuer la prédiction, réduisez l'impact des facteurs défavorables.\n"
|
|
1479
|
+
recommendations += "- Cette explication est basée sur LIME, une technique d'analyse locale du modèle."
|
|
1480
|
+
|
|
1481
|
+
else:
|
|
1482
|
+
# Version anglaise (défaut)
|
|
1483
|
+
if is_classifier:
|
|
1484
|
+
summary = f"Result: {prediction}"
|
|
1485
|
+
if confidence is not None:
|
|
1486
|
+
summary += f" (confidence: {confidence:.1%})\n\n"
|
|
1487
|
+
else:
|
|
1488
|
+
summary += "\n\n"
|
|
1489
|
+
|
|
1490
|
+
if confidence is not None and confidence < 0.7:
|
|
1491
|
+
summary += "Note: The model's confidence in this prediction is relatively low.\n\n"
|
|
1492
|
+
else:
|
|
1493
|
+
summary = f"Result: The predicted value is {prediction}\n\n"
|
|
1494
|
+
|
|
1495
|
+
# Influential factors
|
|
1496
|
+
factors = "Main influential factors:\n"
|
|
1497
|
+
|
|
1498
|
+
if positive_features:
|
|
1499
|
+
factors += "\nPositive factors:\n"
|
|
1500
|
+
for name, importance in positive_features:
|
|
1501
|
+
factors += f"- {name} (impact: +{abs(importance):.2f})\n"
|
|
1502
|
+
|
|
1503
|
+
if negative_features:
|
|
1504
|
+
factors += "\nNegative factors:\n"
|
|
1505
|
+
for name, importance in negative_features:
|
|
1506
|
+
factors += f"- {name} (impact: -{abs(importance):.2f})\n"
|
|
1507
|
+
|
|
1508
|
+
# Recommendations
|
|
1509
|
+
recommendations = "\nRecommendations:\n"
|
|
1510
|
+
if positive_features and not is_classifier:
|
|
1511
|
+
recommendations += "- To increase the prediction, focus on improving the positive factors.\n"
|
|
1512
|
+
elif negative_features and not is_classifier:
|
|
1513
|
+
recommendations += "- To decrease the prediction, reduce the impact of negative factors.\n"
|
|
1514
|
+
recommendations += "- This explanation is based on LIME, a local model analysis technique."
|
|
1515
|
+
|
|
1516
|
+
return summary + factors + recommendations
|
|
1517
|
+
|
|
1518
|
+
def _generate_public_narrative(self, positive_features, negative_features, prediction, is_classifier, language="en"):
|
|
1519
|
+
"""
|
|
1520
|
+
Génère une narrative simplifiée pour le grand public.
|
|
1521
|
+
|
|
1522
|
+
Args:
|
|
1523
|
+
positive_features: Caractéristiques avec impact positif
|
|
1524
|
+
negative_features: Caractéristiques avec impact négatif
|
|
1525
|
+
prediction: Prédiction du modèle (classe ou valeur)
|
|
1526
|
+
is_classifier: Indique si c'est un classificateur ou régresseur
|
|
1527
|
+
language: Langue de l'explication ("en", "fr")
|
|
1528
|
+
|
|
1529
|
+
Returns:
|
|
1530
|
+
str: Narrative publique
|
|
1531
|
+
"""
|
|
1532
|
+
# Limiter à 3 features maximum pour chaque catégorie
|
|
1533
|
+
pos_limited = positive_features[:3]
|
|
1534
|
+
neg_limited = negative_features[:3]
|
|
1535
|
+
|
|
1536
|
+
if language == "fr":
|
|
1537
|
+
# Version française
|
|
1538
|
+
if is_classifier:
|
|
1539
|
+
result = f"Le système a déterminé que le résultat est: {prediction}\n\n"
|
|
1540
|
+
else:
|
|
1541
|
+
result = f"Le système a estimé la valeur suivante: {prediction}\n\n"
|
|
1542
|
+
|
|
1543
|
+
explanation = "Voici pourquoi:\n"
|
|
1544
|
+
|
|
1545
|
+
if pos_limited:
|
|
1546
|
+
explanation += "\nPrincipalement à cause de:\n"
|
|
1547
|
+
for i, (name, _) in enumerate(pos_limited):
|
|
1548
|
+
explanation += f"- {name}\n"
|
|
1549
|
+
|
|
1550
|
+
if neg_limited:
|
|
1551
|
+
explanation += "\nMalgré:\n"
|
|
1552
|
+
for i, (name, _) in enumerate(neg_limited):
|
|
1553
|
+
explanation += f"- {name}\n"
|
|
1554
|
+
|
|
1555
|
+
if not pos_limited and not neg_limited:
|
|
1556
|
+
explanation += "\nLe système n'a pas pu identifier de facteurs clairement déterminants pour cette prédiction."
|
|
1557
|
+
|
|
1558
|
+
else:
|
|
1559
|
+
# Version anglaise (défaut)
|
|
1560
|
+
if is_classifier:
|
|
1561
|
+
result = f"The system has determined that the result is: {prediction}\n\n"
|
|
1562
|
+
else:
|
|
1563
|
+
result = f"The system has estimated the following value: {prediction}\n\n"
|
|
1564
|
+
|
|
1565
|
+
explanation = "Here's why:\n"
|
|
1566
|
+
|
|
1567
|
+
if pos_limited:
|
|
1568
|
+
explanation += "\nMainly because of:\n"
|
|
1569
|
+
for i, (name, _) in enumerate(pos_limited):
|
|
1570
|
+
explanation += f"- {name}\n"
|
|
1571
|
+
|
|
1572
|
+
if neg_limited:
|
|
1573
|
+
explanation += "\nDespite:\n"
|
|
1574
|
+
for i, (name, _) in enumerate(neg_limited):
|
|
1575
|
+
explanation += f"- {name}\n"
|
|
1576
|
+
|
|
1577
|
+
if not pos_limited and not neg_limited:
|
|
1578
|
+
explanation += "\nThe system could not identify clearly determining factors for this prediction."
|
|
1579
|
+
|
|
1580
|
+
return result + explanation
|