Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
AGAS — LLM-driven agentic group shilling attack framework that manipulates black-box collaborative-filtering recommender rankings using adaptive multi-role strategies while evading detection. | Kitploit
Tools/GitHubGitHub/phkhanhtrinh23/agas
Machine LearningPapers & ResearchLearning & EducationAI SecurityAdversarial Attack
GitHubphkhanhtrinh23/agas

AGAS

LLM-driven agentic group shilling attack framework that manipulates black-box collaborative-filtering recommender rankings using adaptive multi-role strategies while evading detection.

View Repository
2103 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

An Efficient and Effective Agentic Group Shilling Attack on Recommender Systems

This is the official code to the paper: "An Efficient and Effective Agentic Group Shilling Attack on Recommender Systems". This paper introduces AGAS, which is an LLM-driven shilling attack against black-box collaborative-filtering recommenders. One Coordinator orchestrates a pool of fake-user workers over a sequence of rounds. In each round, the Coordinator picks one of eight strategies and assigns a role to every worker. Workers then decide which items to rate using their own ReAct-style reasoning loop.

AGAS pipeline


1. Repo overview

root@kitploit:~
agent_attack_rs/
  bash/                           # Reviewer-friendly shell scripts (RQ1–RQ5)
  prompts/                        # Coordinator + per-role prompt templates
  scripts/run_agas.py             # Single entry point (--dataset --victim ...)
  src/agas/
    roles.py                      # Role enum {PR, SN, CA, IN} (paper symbols)
    signals.py                    # WorkerSignals (τ, γ, φ) + EnvSignals (ρ, Δρ, η, ξ, a)
    strategies.py                 # 8-strategy enum
    agents/                       # Coordinator + Worker policies
    simulation/                   # Episode runner (= AGAS algorithm outer loop)
    llm/                          # OpenAI / Ollama
    recsys/                       # Surrogate + 11-victim backends
    data/                         # Dataset loaders + preprocessing pipeline
  tests/                          # Pytest suite

2. Method recap

AGAS instantiates four worker roles (paper symbols in roles.py):

Each round the Coordinator chooses exactly one of eight strategies from strategies.py (see method_strategies.tex):

  1. Victim Probe (S1_VICTIM_PROBE)
  2. Bridge Building (S2_BRIDGE_BUILDING, graph victims only)
  3. Warm-up (S3_WARM_UP)
  4. First Push (S4_FIRST_PUSH)
  5. Silent Slowdown (S5_SILENT_SLOWDOWN)
  6. Profile Cleanup (S6_PROFILE_CLEANUP)
  7. Safe Replacement (S7_SAFE_REPLACEMENT)
  8. Main Attack (S8_MAIN_ATTACK)

The Coordinator drives those decisions from two signal groups (signals.py):

  • Worker signals τ_{t,w}, γ_{t,w}, φ_{t,w} — trust, risk, and a structural validator. Update equations match method_coordinator.tex exactly (eq:trust_update, eq:risk_update, eq:risk_decay, eq:profile_validator).
  • Environment signals ρ^{(t)}, Δρ^{(t)}, η_t, ξ_t = (q_t, s_t), a_t — rank, rank-movement, acceptance rate, suppression signal, alert flag. The suspicion score q_t is the 0.2-weighted sum of the five normalised terms (d̂_t, δ̂_t, m̂_t, ŝ_t, g_t) from eq:round_suppression_terms and eq:round_suppression_score.

Round loop (ASCII)

root@kitploit:~
                ┌─────────────────────────────────────────────────┐
   t=0…T-1 ──►  │ 1. Observe ρ^{(t)}, update memory m_t           │
                │ 2. Update τ, γ, φ, η, ξ, a                       │
                │ 3. Coordinator picks Strategy ∈ {S1…S8}          │
                │    and assigns Role ∈ {PR, SN, CA, IN} per worker│
                │ 4. Workers act (filler / bridge / target items)  │
                │ 5. Validate + accept actions → ΔR̃^{(t+1)}        │
                │ 6. Refit / query victim → ρ^{(t+1)}              │
                └─────────────────────────────────────────────────┘
                          │
                          ▼
                   t* = argmin_t ρ^{(t)}, return R* = [R ; R̃^{(≤t*)}]

The outer loop is implemented in src/agas/simulation/episode.py and mirrors algorithms/agas_end_to_end.tex.

3. Environment

PyTorch and CUDA are only required for the deep-learning victim models ([targets] extra). The core AGAS loop and the rule-based / surrogate paths run on CPU with no GPU dependency.

4. Install

root@kitploit:~
pip install -e .
# If you want to use the deep-learning victim models (LightGCN, NeuMF, …)
pip install -e '.[targets]'

Required environment variables:

VariablePurposeDefault
OPENAI_API_KEYOpenAI Responses API key for the Coordinator / worker LLMs.(unset → fallback)
OPENAI_MODELModel name passed to OpenAI.gpt-5.1

4. Datasets

The paper evaluates on six public CF benchmarks (see experiment.tex):

After dropping the raw downloads into data/<dataset>/, run:

root@kitploit:~
python scripts/preprocess_all.py --data-root data --output-root processed

Each dataset is rewritten into canonical interactions.csv + items.csv files under processed/<dataset>/. The smoke tests use the much smaller ml-latest-small sample that ships with MovieLens.

Train / test split protocol

The preprocessing pipeline stores no split files — it exports the full interaction log. Splits are applied at runtime:

This follows the standard shilling-attack evaluation protocol: the attacker observes the victim's rankings on training-set users and optimises accordingly, mimicking a black-box deployment scenario.

5. How to run

scripts/run_agas.py is the single entry point. The seven flags required by the paper protocol are:

root@kitploit:~
python scripts/run_agas.py \
    --dataset ml-100k \
    --victim lightgcn \
    --rounds 18 \
    --seed 42 \
    --n_workers 8 \
    --budget 0.01 \
    --out outputs/agas_ml100k_lightgcn_seed42.json

Reproducing each research question on a tiny ML-100K sample

root@kitploit:~
bash bash/run_performance.sh          # RQ1
bash bash/run_stealth_and_detect.sh   # RQ2 + RQ3
bash bash/run_ablation.sh             # RQ4
bash bash/run_efficiency.sh           # RQ5

Each script prints which experiment it is running, the output path, and a note pointing to the corresponding figure/table in the paper.

Example expected console output (truncated):

root@kitploit:~
[RQ1] Performance benchmark — tiny ML-100K sample
[RQ1] Output directory: agent_attack_rs/outputs/rq1_performance
[RQ1] Map outputs to tables/benchmark_unpopular.tex
=== ml-latest-small / lightgcn / seed=42 ===
Round  0 | ρ = 1834  Δρ = +0     strategy = S1_VICTIM_PROBE
Round  1 | ρ = 1450  Δρ = +384   strategy = S1_VICTIM_PROBE
Round  2 | ρ = 1212  Δρ = +238   strategy = S3_WARM_UP
…
[RQ1] Done. Expected outputs in agent_attack_rs/outputs/rq1_performance

Transfer attack against LightGCN (in-loop)

Runs LightGCN as both the episode model and evaluation target. The victim retrains after every round so the Coordinator receives real rank feedback and adapts roles and strategy accordingly.

root@kitploit:~
agas run-transfer \
  --dataset ml-latest-small \
  --target-item-id 7114 \
  --target-keyword horror \
  --num-agents 50 \
  --num-steps 10 \
  --victim-model-hint lightgcn \
  --profiler-bridge-method cooccurrence \
  --probe-steps 0 \
  --graph-sniper \
  --clone-segment-users-to-agents \
  --transfer-mode option-b \
  --target-models lightgcn \
  --target-epochs 50 \
  --target-embedding-dim 32 \
  --target-lightgcn-layers 3 \
  --min-active-fraction 0.5 \
  --min-sniper-fraction 0.3 \
  --output outputs/optb_warmstart_clean_cooc_50ag_10r.json

FlagMeaning
--profiler-bridge-method cooccurrenceBridge items selected by co-occurrence with the target's rater cluster.
--graph-sniperSnipers rate bridge items and the target directly (dual action).

After the run, results are printed and saved to the output JSON:

root@kitploit:~
Bridge-item selection (cooccurrence): 50 items selected for profiler pool.
...
Transfer evaluation saved to outputs/optb_warmstart_clean_cooc.json
Option B (in-loop target models):
  lightgcn: final rank <final> (best in-loop <best> at step <step>, best-seq retrain rank <bsr>)

6. Token logging, role activation & strategy tracking

Every run-episode call automatically computes and logs three types of analytics:

6a. Token usage

Tokens are counted per LLM call and accumulated across the episode for the coordinator and for each worker agent separately. They are printed to the console after the episode and saved inside the output JSON under token_usage.

Console output example:

root@kitploit:~
--- Token Usage ---
  Coordinator : prompt=20,043  completion=2,526  total=22,569
  agent_1     : prompt=1,308   completion=619    total=1,927
  agent_2     : prompt=3,930   completion=451    total=4,381
  agent_3     : prompt=0       completion=0      total=0
  AGGREGATE   : prompt=25,281  completion=3,596  total=28,877
-------------------

JSON structure (output["token_usage"]):

root@kitploit:~
{
  "coordinator": {"prompt_tokens": 20043, "completion_tokens": 2526, "total_tokens": 22569},
  "by_agent":    {"agent_1": {"prompt_tokens": 1308, ...}, ...},
  "aggregate":   {"prompt_tokens": 25281, "completion_tokens": 3596, "total_tokens": 28877}
}

Reading token counts from a saved run:

root@kitploit:~
import json
with open("outputs/my_run.json") as f:
    data = json.load(f)

agg = data["token_usage"]["aggregate"]
print(f"Total tokens used: {agg['total_tokens']:,}")
print(f"  Prompt     : {agg['prompt_tokens']:,}")
print(f"  Completion : {agg['completion_tokens']:,}")

# Per-step token usage (inside each worker trace)
for step in data["history"]:
    for report in step["reports"]:
        tok = (report.get("trace") or {}).get("token_usage") or {}
        print(f"Step {step['step']} {report['agent_id']}: {tok.get('total_tokens', 0)} tokens")

Provider notes:

ProviderFields read
OpenAI Responses APIresponse.usage.input_tokens, .output_tokens
Ollama /api/generatedata["prompt_eval_count"], data["eval_count"]

6b. Role activation counts

After each episode, the CLI counts how many times each role was assigned across all agents and all steps. Printed to console and saved under output["activation_stats"]["role_counts"].

Console output example:

root@kitploit:~
--- Role Activations ---
  inactive          : 10
  profiler          : 4
--- Role Activations by Agent ---
  agent_1     : inactive=3  profiler=1  
  agent_2     : profiler=3  inactive=2
  agent_3     : inactive=5

Reading role counts programmatically:

root@kitploit:~
stats = data["activation_stats"]
print("Most used role:", max(stats["role_counts"], key=stats["role_counts"].get))
print("Role counts:", stats["role_counts"])
print("By agent:", stats["role_counts_by_agent"])

6c. Strategy activation counts

The coordinator's role-assignment pattern is mapped to one of the eight paper strategies (S1–S8) each step using the following inference rules:

Saved under output["activation_stats"]["strategy_counts"].

Console output example:

root@kitploit:~
--- Strategy Activations ---
  S1_VICTIM_PROBE          : 1
  S3_WARM_UP               : 4

6d. Per-run human-readable log

A .log file is written alongside every output JSON automatically (e.g. outputs/my_run.log). It contains:

  • Episode metadata (dataset, target, agent count, policies)
  • Per-step timeline: rank, strategy, each agent's role, actions, and per-call token count
  • Token usage summary
  • Role and strategy activation tallies

Running the CLI and reading the log:

root@kitploit:~
agas run-episode \
  --dataset ml-latest-small \
  --target-item-id 2571 \
  --target-keyword "Matrix" \
  --num-agents 3 \
  --num-steps 10 \
  --coordinator-policy openai \
  --worker-policy openai \
  --output outputs/run_matrix.json

# Human-readable log is at:
cat outputs/run_matrix.log

# JSON output is at:
python -c "
import json
d = json.load(open('outputs/run_matrix.json'))
print('Token aggregate:', d['token_usage']['aggregate'])
print('Role counts:',    d['activation_stats']['role_counts'])
print('Strategy counts:',d['activation_stats']['strategy_counts'])
"

7. Protocol and prompts

The Coordinator and every worker are LLM agents that read from prompts/<role>/{system,user}.txt. The full protocol — what the Coordinator interpolates into its prompt, which variables the workers see, and the expected JSON output schemas — is in prompts/README.md.

Brief summary:

  • Coordinator → JSON with {"strategy": "S?_…", "assignments": {agent: ROLE}}.
  • Workers → JSON with {"actions": [{"item_id": …, "rating": …, "reason": …}]}.
  • Allowed item pools are role-dependent (filler pool, bridge pool, target + competitors) and enforced by the host code.

8. Reproduced results from the paper

All numbers below are mean ± 95 % CI over 5 seeds, scaled by 10³ (i.e., a cell of 40.0±0.2 means HR@10 = 0.0400 ± 0.0002). Bold = best, italic = second best, matching tables/benchmark_unpopular.tex and tables/detection_mf.tex in the paper.

Unpopular-target promotion (tables/benchmark_unpopular.tex)

Reproduce with:

root@kitploit:~
bash bash/run_performance.sh          # RQ1; writes outputs/rq1_performance/

Embedding-based victims (cell = H@10 / NDCG@10):

Graph-based victims (cell = H@10 / NDCG@10):

9. License

MIT License.

Download Tool
SymbolLong nameWhat it does
PRProfilerSafe filler-item ratings to probe the platform and build bridge pools.
SNSniperPayload role; direct target push or bridge-item promotion.
CACamouflageurStealth role; rebuilds trust with benign-looking activity.
INInactiveNo action this round (cool-down or quarantine).
ComponentRequirementTested with
Python≥ 3.103.13.5
PyTorch≥ 2.1 (targets only)2.11.0+cu128
CUDAoptional12.8
NumPy≥ 1.242.4.2
Pandas≥ 2.03.0.1
SciPy≥ 1.101.17.0
scikit-learn≥ 1.31.8.0
openai SDK≥ 1.122.21.0
Short nameSourceUsersItemsInteractionsDownloadPlace raw files in
ML-100KMovieLens 100K9431,682100,000GroupLensdata/ml-100k/
ML-1MMovieLens 1M6,0403,7061,000,209GroupLensdata/ml-1m/
Genome 2021MovieLens Tag Genome 202137,94184,6612,000,000 (capped)GroupLensdata/genome2021/
NetflixNetflix Prize342,44517,4342,000,000 (capped)Kaggledata/netflix/
DoubanDouban Movie28,05749,1768,085,679HKUSTdata/douban/
AmazonAmazon Reviews 2018998,65330,9642,000,000 (capped)UCSDdata/amazon/
StageData usedDetails
TrainingAll interactions in interactions.csvThe surrogate (and any target model) is fit on the complete set of historical ratings.
Attack evaluationRanking over segment usersAfter each round, the target item's mean rank is measured across real benign users who rated ≥ 4.0 on at least one target-cluster item (up to 2 000 users). No held-out test set is written to disk.
Fake injectionAppended in-memoryFake-user interactions are appended and the model is incrementally re-fit each round. They are never mixed into the canonical CSV files.
FlagMeaning
--datasetOne of ml-100k, ml-1m, genome2021, netflix, douban, amazon, ml-latest-small.
--victimOne of the 11 victims from experiment.tex (mf, bpr, neumf, gmf, ncf, ngcf, lightgcn, simgcl, xsimgcl, egcf, lightccf).
--roundsNumber of AGAS rounds T (paper default 18).
--seedRandom seed (paper averages over five).
--n_workersFake-user pool size
--budgetPer-user interaction budget L, expressed as a fraction of.
--outOutput JSON path for the full episode trace and summary metrics.
ConditionInferred strategy
Probe phase / diagnostic agent activeS1_VICTIM_PROBE
Alert flag set (a_t = 1)S7_SAFE_REPLACEMENT
Max suspicion score ≥ 0.5S6_PROFILE_CLEANUP
No snipers + visible discountingS5_SILENT_SLOWDOWN
No snipers + no discountingS3_WARM_UP
Snipers activeS4_FIRST_PUSH
Snipers activeS8_MAIN_ATTACK
MethodML-100K·MF(BPR)ML-100K·NeuMFML-1M·GMFML-1M·NCFAmazon·MF(BPR)Amazon·NCFGenome·GMFGenome·NeuMFNetflix·MF(BPR)Netflix·NeuMF
NoneAttack1.8±0.2 / 0.7±0.52.2±0.2 / 0.9±0.60.9±0.1 / 0.4±0.30.8±0.1 / 0.3±0.20.4±0.1 / 0.1±0.20.5±0.1 / 0.2±0.20.2±0.1 / 0.1±0.20.5±0.1 / 0.2±0.20.6±0.1 / 0.2±0.20.7±0.1 / 0.3±0.2
RandomAttack4.1±0.3 / 1.6±0.64.7±0.3 / 1.8±0.62.4±0.2 / 1.0±0.52.0±0.2 / 0.8±0.41.2±0.1 / 0.5±0.31.3±0.1 / 0.5±0.30.7±0.1 / 0.3±0.21.3±0.1 / 0.5±0.31.8±0.2 / 0.7±0.42.0±0.2 / 0.8±0.5
BandwagonAttack6.2±0.3 / 2.5±0.76.8±0.3 / 2.7±0.83.4±0.2 / 1.4±0.62.9±0.2 / 1.2±0.52.0±0.1 / 0.8±0.42.2±0.1 / 0.9±0.41.2±0.1 / 0.5±0.32.0±0.1 / 0.8±0.42.7±0.2 / 1.1±0.53.0±0.2 / 1.2±0.5
AUSH10.6±0.6 / 4.3±1.210.0±0.5 / 4.1±1.16.5±0.4 / 2.6±0.94.3±0.3 / 1.8±0.73.3±0.3 / 1.3±0.63.1±0.3 / 1.2±0.61.9±0.2 / 0.8±0.52.8±0.3 / 1.1±0.64.2±0.3 / 1.7±0.74.5±0.3 / 1.8±0.8
PoisonRec13.4±0.7 / 5.3±1.611.5±0.6 / 4.7±1.57.6±0.5 / 3.0±1.15.5±0.4 / 2.1±0.95.8±0.5 / 2.3±1.04.8±0.3 / 1.9±0.82.3±0.3 / 0.9±0.63.2±0.3 / 1.3±0.75.6±0.4 / 2.2±1.05.7±0.4 / 2.3±1.0
PGA11.5±0.6 / 4.6±1.312.2±0.6 / 5.0±1.48.4±0.5 / 3.4±1.16.0±0.4 / 2.4±0.94.3±0.3 / 1.7±0.84.0±0.3 / 1.6±0.72.8±0.2 / 1.1±0.63.8±0.3 / 1.5±0.75.5±0.4 / 2.2±1.06.0±0.5 / 2.4±1.0
AgentSA12.7±0.6 / 5.2±1.412.5±0.6 / 5.1±1.48.2±0.4 / 3.3±1.07.2±0.4 / 2.9±1.05.6±0.3 / 2.2±0.94.9±0.3 / 1.9±0.82.9±0.2 / 1.1±0.64.2±0.3 / 1.7±0.76.3±0.4 / 2.5±1.06.5±0.4 / 2.6±1.0
AgentAttack13.6±0.7 / 5.5±1.512.1±0.6 / 4.9±1.48.8±0.5 / 3.5±1.16.8±0.4 / 2.7±1.05.7±0.3 / 2.2±0.95.2±0.3 / 2.0±0.92.7±0.2 / 1.0±0.64.5±0.3 / 1.8±0.86.1±0.4 / 2.4±1.06.9±0.5 / 2.8±1.0
AGAS (Ours)40.0±0.2 / 16.1±0.335.0±0.2 / 14.2±0.322.6±0.1 / 9.0±0.217.6±0.1 / 7.1±0.216.1±0.1 / 6.3±0.215.0±0.1 / 5.9±0.28.2±0.1 / 3.2±0.211.5±0.1 / 4.6±0.218.2±0.1 / 7.3±0.219.6±0.1 / 7.9±0.2
Improvement+187.8% / +182.5%+186.9% / +184.0%+162.8% / +164.7%+155.1% / +153.6%+177.6% / +173.9%+172.7% / +168.2%+192.9% / +190.9%+161.4% / +155.6%+198.4% / +192.0%+192.5% / +192.6%
MethodML-100K·NGCFML-100K·LightGCNML-1M·SimGCLML-1M·XSimGCLAmazon·EGCFAmazon·LightCCFGenome·NGCFGenome·LightGCNDouban·NGCFDouban·LightGCN
NoneAttack1.9±0.2 / 0.8±0.52.4±0.2 / 1.0±0.60.8±0.1 / 0.3±0.20.9±0.1 / 0.4±0.30.4±0.1 / 0.2±0.20.5±0.1 / 0.2±0.20.2±0.1 / 0.1±0.20.3±0.1 / 0.1±0.20.7±0.1 / 0.3±0.20.6±0.1 / 0.2±0.2
RandomAttack4.5±0.3 / 1.8±0.65.0±0.3 / 2.0±0.71.8±0.2 / 0.7±0.42.2±0.2 / 0.9±0.51.1±0.1 / 0.4±0.31.2±0.1 / 0.5±0.30.6±0.1 / 0.2±0.20.6±0.1 / 0.2±0.22.0±0.2 / 0.8±0.51.9±0.2 / 0.7±0.4
BandwagonAttack5.9±0.3 / 2.4±0.76.7±0.3 / 2.7±0.82.4±0.2 / 1.0±0.52.9±0.2 / 1.2±0.61.8±0.2 / 0.7±0.41.9±0.2 / 0.8±0.41.1±0.1 / 0.4±0.31.0±0.1 / 0.4±0.32.8±0.2 / 1.1±0.62.7±0.2 / 1.1±0.5
GSPAttack11.0±0.6 / 4.5±1.311.5±0.6 / 4.7±1.34.8±0.3 / 1.9±0.85.8±0.4 / 2.3±0.93.2±0.3 / 1.3±0.63.4±0.3 / 1.4±0.62.0±0.2 / 0.8±0.51.8±0.2 / 0.7±0.55.0±0.3 / 2.0±0.84.8±0.3 / 1.9±0.8
TargetedAttack11.9±0.6 / 4.8±1.312.6±0.6 / 5.1±1.45.4±0.4 / 2.2±0.86.4±0.4 / 2.5±1.04.7±0.3 / 1.9±0.74.5±0.3 / 1.8±0.72.5±0.2 / 1.0±0.62.2±0.2 / 0.9±0.55.4±0.4 / 2.2±0.85.2±0.3 / 2.1±0.8
CLeaR13.1±0.7 / 5.4±1.413.9±0.7 / 5.7±1.56.4±0.4 / 2.6±0.97.7±0.5 / 3.1±1.04.6±0.3 / 1.8±0.75.2±0.4 / 2.1±0.82.9±0.3 / 1.2±0.62.8±0.2 / 1.1±0.65.8±0.4 / 2.3±0.95.5±0.3 / 2.2±0.9
AgentSA13.8±0.6 / 5.6±1.414.2±0.7 / 5.8±1.56.1±0.4 / 2.4±0.88.4±0.5 / 3.3±1.04.5±0.3 / 1.8±0.75.4±0.3 / 2.2±0.82.8±0.2 / 1.1±0.62.9±0.2 / 1.1±0.65.4±0.3 / 2.2±0.85.8±0.4 / 2.3±0.9
AgentAttack14.4±0.7 / 5.9±1.514.0±0.7 / 5.6±1.56.6±0.4 / 2.6±0.98.0±0.5 / 3.1±1.04.9±0.3 / 1.9±0.75.0±0.3 / 2.0±0.83.1±0.3 / 1.2±0.62.5±0.2 / 1.0±0.56.0±0.4 / 2.4±0.95.5±0.3 / 2.2±0.9
AGAS (Ours)40.0±0.2 / 17.0±0.340.5±0.2 / 16.5±0.316.8±0.1 / 6.7±0.220.7±0.1 / 8.2±0.213.1±0.1 / 5.2±0.213.8±0.1 / 5.4±0.28.9±0.1 / 3.4±0.27.8±0.1 / 3.1±0.217.0±0.1 / 6.8±0.216.9±0.1 / 6.7±0.2
Improvement+181.7% / +193.1%+189.3% / +194.6%+162.5% / +157.7%+155.6% / +156.2%+178.7% / +173.7%+165.4% / +157.1%+196.7% / +183.3%+178.6% / +181.8%+193.1% / +195.7%+196.5% / +191.3%