<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ferdinand Boas</title>
    <description>The latest articles on DEV Community by Ferdinand Boas (@ferdi05).</description>
    <link>https://dev.to/ferdi05</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F536105%2Fdf9be946-439a-403c-a358-dec5b108114d.jpeg</url>
      <title>DEV Community: Ferdinand Boas</title>
      <link>https://dev.to/ferdi05</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ferdi05"/>
    <language>en</language>
    <item>
      <title>Comparing Secrets Detection Solutions? Here’s Why You Should Use the F1 Score</title>
      <dc:creator>Ferdinand Boas</dc:creator>
      <pubDate>Mon, 03 Feb 2025 15:25:54 +0000</pubDate>
      <link>https://dev.to/gitguardian/comparing-secrets-detection-solutions-heres-why-you-should-use-the-f1-score-2ndi</link>
      <guid>https://dev.to/gitguardian/comparing-secrets-detection-solutions-heres-why-you-should-use-the-f1-score-2ndi</guid>
      <description>&lt;p&gt;As organizations increasingly adopt DevOps practices, the need for reliable secrets detection solutions has never been greater. However, not all tools are created equal. With so many options available, how can you determine which one best answers your needs? Comparing tools can be surprisingly complex.&lt;/p&gt;

&lt;p&gt;In this post, we’ll introduce the &lt;strong&gt;F1 score&lt;/strong&gt; as the most reliable metric for evaluating their performance. By balancing &lt;a href="https://blog.gitguardian.com/precision-recall-security-zines/" rel="noopener noreferrer"&gt;recall&lt;/a&gt; (how many valid secrets a tool catches) and &lt;a href="https://blog.gitguardian.com/precision-recall-security-zines/" rel="noopener noreferrer"&gt;precision&lt;/a&gt; (how often the secret is valid), the F1 score provides a comprehensive and fair way to assess the effectiveness of secrets detection solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Comparing Secrets Detection Solutions is Hard
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://blog.gitguardian.com/secrets-detection-accuracy-precision-recall-explained/" rel="noopener noreferrer"&gt;When evaluating secrets detection solutions&lt;/a&gt;, two core metrics play a pivotal role:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Recall&lt;/strong&gt;: Measures how many valid secrets a tool successfully detects out of the total secrets that exist. A tool with high recall is less likely to miss exposed secrets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Precision&lt;/strong&gt;: Indicates how often a detected secret is actually valid. High precision means fewer false positives.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F90h7dk0bapuefx7itgmb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F90h7dk0bapuefx7itgmb.png" alt="Recall and Precision role in secrets detection" width="800" height="341"&gt;&lt;/a&gt;&lt;br&gt;Recall and Precision role in secrets detection.
  &lt;/p&gt;

&lt;p&gt;Achieving high recall without sacrificing precision is notoriously tricky. Tools with high recall but poor precision risk overwhelming users with false positives, creating inefficiencies, and eroding trust in the solution. On the other hand, tools with high precision but low recall might miss critical secrets, leaving organizations vulnerable to breaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding a solution that strikes the right balance between these two metrics is essential for effective secrets detection.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the F1 Score
&lt;/h2&gt;

&lt;p&gt;Imagine a secrets detection solutions with &lt;strong&gt;precision = 90%&lt;/strong&gt; and &lt;strong&gt;recall = 40%&lt;/strong&gt;. If we calculate the arithmetic mean of these metrics to evaluate the overall performance of this tool, we will get:

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Precision+Recall2=90+402=65\frac{\text{Precision} + \text{Recall}}{2} = \frac{90 + 40}{2} = 65% &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Precision&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Recall&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;90&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;40&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;65&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;
At first glance, this 65% score seems reasonable. However, it &lt;strong&gt;overemphasizes the higher metric (precision)&lt;/strong&gt; and &lt;strong&gt;ignores the gap in recall&lt;/strong&gt;, which reflects missed critical secrets.

&lt;p&gt;Other types of means exist. The harmonic mean, rather than the simple arithmetic mean, is particularly useful for datasets with imbalanced metrics, like when precision and recall vary significantly. Here, it would be
&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Harmonic Mean=2×(Precision×Recall)Precision+Recall=2×(90×40)90+40=55.38%\text{Harmonic Mean} = \frac{2 \times (\text{Precision} \times \text{Recall})}{\text{Precision} + \text{Recall}} = \frac{2 \times (90 \times 40)}{90 + 40} = 55.38\% &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Harmonic Mean&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Precision&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Recall&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Precision&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Recall&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;90&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;40&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;90&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;40&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;55.38%&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;
This 55% score feels very different; you probably won’t trust this solution.

&lt;p&gt;This score highlights the real-world challenge: &lt;strong&gt;high performance in one area cannot compensate for poor performance in another&lt;/strong&gt;.&lt;br&gt;
 &lt;br&gt;
That’s how the F1 score was built. The F1 score is the &lt;strong&gt;harmonic mean of recall and precision&lt;/strong&gt;, widely used when working on predicting algorithm.
&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;F1=2×(Precision×Recall)Precision+Recall\text{F1} = \frac{2 \times (\text{Precision} \times \text{Recall})}{\text{Precision} + \text{Recall}} &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;F1&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Precision&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Recall&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Precision&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Recall&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;
By penalizing extreme differences between recall and precision, the F1 score allows organizations to compare solutions &lt;strong&gt;fairly&lt;/strong&gt; without overemphasizing one parameter at the expense of the other.

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxt4wdz4nl2tdpr2rypov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxt4wdz4nl2tdpr2rypov.png" alt="Good F1 scores are in red, bad ones in blue. Being in the red zone requires both good precision and recall. The F1 score of the evaluated tool is plotted here, among the green scores, far away from the red scores" width="741" height="683"&gt;&lt;/a&gt;&lt;br&gt;Good F1 scores are in red, bad ones in blue. Being in the red zone requires both good precision and recall. The F1 score of the evaluated tool is plotted here, among the green scores, far away from the red scores.
  &lt;/p&gt;

&lt;p&gt;By balancing the risks of &lt;strong&gt;missed secrets&lt;/strong&gt; and &lt;strong&gt;team overload&lt;/strong&gt;, the F1 score provides a practical way to rank tools based on their overall performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  When You Might Prioritize Recall or Precision Over F1
&lt;/h2&gt;

&lt;p&gt;There are cases where one metric matters more than their combination:&lt;/p&gt;

&lt;h3&gt;
  
  
  Focusing on Recall
&lt;/h3&gt;

&lt;p&gt;In high-stakes scenarios like post-breach investigations or audits, missing even one secret can be catastrophic. Maximizing recall ensures every potential secret is flagged, even if it results in more false positives that require investigation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Focusing on Precision
&lt;/h3&gt;

&lt;p&gt;When reducing false positives is critical—such as in a SOC handling numerous alerts or workflows that automatically generate Jira tickets upon incident detection—high precision helps prevent alert fatigue and streamlines operations. Slightly lower recall is acceptable if it means focusing on actionable incidents without overwhelming teams.&lt;/p&gt;

&lt;p&gt;Ultimately, your priorities—avoiding noise or catching every secret—should guide whether precision or recall takes precedence. For most mature enterprises, both high recall and precision are required in the long run, &lt;strong&gt;meaning a high F1 score&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Compare F1 Scores and Performance of Solutions
&lt;/h2&gt;

&lt;p&gt;Evaluating the performance of secret detection solutions is inherently challenging because there is no perfect, hand-labeled dataset of secrets available for benchmarking. The main reasons include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Data Sensitivity&lt;/strong&gt; – No organization will publicly share a dataset containing real secrets, as doing so would be a major security risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataset Limitations&lt;/strong&gt; – Any publicly available dataset will either be artificially generated or fail to replicate the real-world conditions of an organization.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As a result, benchmarks must rely on actual datasets from real-world scenarios. This introduces a fundamental limitation: we cannot determine the absolute recall of a tool because the total number of real secrets in the dataset is unknown. Labeling datasets at a scale large enough to perform unbiased comparison would be prohibitively time-consuming and resource-intensive.&lt;/p&gt;

&lt;p&gt;While this approach does not provide an absolute measure of performance, it allows for meaningful comparisons between solutions by focusing on their effectiveness in detecting real secrets under the same conditions. When a solution detects a secret, we can validate it by attempting to use the secrets (ethically and in controlled environments) to confirm whether they work or, if feasible,by checking with the platform or service provider to confirm validity.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Compute the F1 Score: An Example
&lt;/h2&gt;

&lt;p&gt;Let’s take an example of four different secret detection solutions evaluated on the same dataset of real-world data. From this evaluation, you can gather two key pieces of information:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The total number of secrets detected by each solution (their findings).&lt;/li&gt;
&lt;li&gt;The number of valid secrets detected (true positives), after testing and validating each finding.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here are the results from this experiment.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Solution&lt;/th&gt;
      &lt;th&gt;Total of secrets detected&lt;/th&gt;
      &lt;th&gt;Number of valid secrets detected (True Positives)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution A&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;3,000&lt;/td&gt;
      &lt;td&gt;2,700&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution B&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;9,000&lt;/td&gt;
      &lt;td&gt;6,500&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution C&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;7,000&lt;/td&gt;
      &lt;td&gt;6,000&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution D&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;5,000&lt;/td&gt;
      &lt;td&gt;4,600&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 1: Calculating Precision
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Precision&lt;/strong&gt; measures how many of the detected secrets are valid. It is calculated using the formula  
&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Precision=True PositivesTrue Positives+False Positives\text{Precision} = \frac{\text{True Positives}}{\text{True Positives} + \text{False Positives}} &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Precision&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;True Positives&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;False Positives&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;True Positives&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;
In other words, it’s the ratio of valid secrets to the total secrets detected by the tool.

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Solution&lt;/th&gt;
      &lt;th&gt;Number of valid secrets detected (True Positives)&lt;/th&gt;
      &lt;th&gt;Number of invalid secrets detected (False Positives)&lt;/th&gt;
      &lt;th&gt;Precision&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution A&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;2,700&lt;/td&gt;
      &lt;td&gt;300&lt;/td&gt;
      &lt;td&gt;90.0%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution B&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;6,500&lt;/td&gt;
      &lt;td&gt;2,500&lt;/td&gt;
      &lt;td&gt;72.2%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution C&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;6,000&lt;/td&gt;
      &lt;td&gt;1,000&lt;/td&gt;
      &lt;td&gt;85.7%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution D&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;4,600&lt;/td&gt;
      &lt;td&gt;400&lt;/td&gt;
      &lt;td&gt;&lt;b&gt;92.0%&lt;/b&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If we look at precision alone, Solution D appears to be the best, with 92% of its findings being valid. However, it also misses many valid secrets that were detected by Solutions B and C. That’s the reason why you need to consider their &lt;strong&gt;recall&lt;/strong&gt; as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Calculating Recall
&lt;/h3&gt;

&lt;p&gt;Recall measures how many of the actual secrets in the dataset are detected. It is defined by 
&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Recall=True PositivesTrue Positives+False Negatives\text{Recall} = \frac{\text{True Positives}}{\text{True Positives} + \text{False Negatives}} &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Recall&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;True Positives&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;False Negatives&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;True Positives&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;
It’s the Number of valid secrets detected divided by the Total number of actual secrets in the dataset.

&lt;p&gt;Here, we face a challenge: determining the &lt;strong&gt;total number of actual secrets&lt;/strong&gt; in datasets large enough for meaningful comparisons, is prohibitively expensive. This number remains unknown in real-world scenarios because no hand-labeled dataset fully replicates actual data. So, how do we calculate recall?&lt;/p&gt;

&lt;p&gt;Let’s look at the distribution of secrets.&lt;br&gt;
When secret detection solutions analyze a dataset, they do not detect the identical set of secrets. Each solution has different detection capabilities, leading to variations in their findings. Some secrets may be detected by multiple solutions, while others might be unique to a single tool. This overlap, and the differences between findings, are key to understanding the dataset's composition.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fip4r30xxd5qk2kr52zy3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fip4r30xxd5qk2kr52zy3.png" alt="Example distribution of secrets detected by various solutions benchmarked" width="800" height="691"&gt;&lt;/a&gt;&lt;br&gt;Example distribution of secrets detected by various solutions benchmarked.
  &lt;/p&gt;

&lt;p&gt;Since we do not have a ground-truth dataset with pre-labeled secrets, the best assumption we can make is that the total number of actual secrets in the dataset corresponds to the &lt;strong&gt;union of all valid secrets detected across all solutions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this example, there are many findings\ 
&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Total Findings=100+2700+400+600+300+4000+2000=10,100\text{Total Findings} = 100 + 2700 + 400 + 600 + 300 + 4000 + 2000 = 10,100 &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Total Findings&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;100&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2700&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;400&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;600&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;300&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;4000&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2000&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;10&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;100&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;
To determine the actual number of secrets in the dataset, all detected findings need to be validated. While this approach is an approximation, it is the most reliable method available. Even when combining the findings from all solutions, some secrets may still be missed. However, this approximation provides a solid baseline for &lt;strong&gt;fairly comparing&lt;/strong&gt; the performance of each solution relative to the others.

&lt;p&gt;For this example, we assume the dataset contains &lt;strong&gt;8,000 actual secrets&lt;/strong&gt;—a reasonable midpoint based on validation results.&lt;/p&gt;

&lt;p&gt;Interestingly, Solution B reported &lt;strong&gt;9,000 findings&lt;/strong&gt;, more than our estimated total of actual secrets. This suggests that Solution B’s detectors &lt;strong&gt;over-identified&lt;/strong&gt; secrets, likely misclassifying some non-sensitive data as secrets, generating even more false positives.&lt;/p&gt;

&lt;p&gt;Here’s the recall calculation for each solution.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Solution&lt;/th&gt;
      &lt;th&gt;Number of valid secrets detected (True Positives)&lt;/th&gt;
      &lt;th&gt;Number of valid secrets NOT detected (False Negatives)&lt;/th&gt;
      &lt;th&gt;Recall&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution A&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;2,700&lt;/td&gt;
      &lt;td&gt;5,300&lt;/td&gt;
      &lt;td&gt;33.8%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution B&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;6,500&lt;/td&gt;
      &lt;td&gt;1,500&lt;/td&gt;
      &lt;td&gt;&lt;b&gt;83.1%&lt;/b&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution C&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;6,000&lt;/td&gt;
      &lt;td&gt;2,000&lt;/td&gt;
      &lt;td&gt;75.0%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution D&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;4,600&lt;/td&gt;
      &lt;td&gt;3,400&lt;/td&gt;
      &lt;td&gt;57.5%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If we look at recall alone, Solution B is the top performer, detecting 83.1% of the actual secrets. However, it comes with a drawback: &lt;strong&gt;2,500 false positives&lt;/strong&gt;, which could overwhelm security teams with invalid alerts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Calculating the F1 Score
&lt;/h3&gt;

&lt;p&gt;Now, let’s compute the respective F1 score of all solutions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Solution&lt;/th&gt;
      &lt;th&gt;Recall&lt;/th&gt;
      &lt;th&gt;Precision&lt;/th&gt;
      &lt;th&gt;F1 Score&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution A&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;33.8%&lt;/td&gt;
      &lt;td&gt;90.0%&lt;/td&gt;
      &lt;td&gt;49.1%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution B&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;83.1%&lt;/td&gt;
      &lt;td&gt;72.2%&lt;/td&gt;
      &lt;td&gt;76.5%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution C&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;75.0%&lt;/td&gt;
      &lt;td&gt;85.7%&lt;/td&gt;
      &lt;td&gt;&lt;b&gt;80.0%&lt;/b&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;b&gt;Solution D&lt;/b&gt;&lt;/td&gt;
      &lt;td&gt;57.5%&lt;/td&gt;
      &lt;td&gt;92.0%&lt;/td&gt;
      &lt;td&gt;70.8%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Solution C&lt;/strong&gt; has the highest F1 score in this example, making it the best overall solution. Although it doesn’t have the highest recall or precision, it strikes the best balance between the two, detecting many valid secrets while keeping false positives relatively low.&lt;/p&gt;

&lt;p&gt;This demonstrates the importance of looking beyond a single metric (precision or recall) and considering the F1 score when comparing secret detection solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Some Nuance: Why Some Secrets Matter More
&lt;/h2&gt;

&lt;p&gt;Not all secrets carry the same level of risk. &lt;strong&gt;Cloud provider credentials&lt;/strong&gt; and &lt;strong&gt;identity service secrets&lt;/strong&gt; are among the most critical for enterprises and other organizations, as they can grant attackers broad access to highly sensitive systems. In contrast, an &lt;strong&gt;API key for a music streaming service&lt;/strong&gt; may pose minimal risk to an organization.&lt;/p&gt;

&lt;p&gt;To assess a tool’s effectiveness, enterprises &lt;strong&gt;may prioritize evaluating its performance on the types of secrets that matter most to them&lt;/strong&gt;. This means focusing on categories of high-risk secrets—such as cloud credentials, database passwords, or private keys—rather than treating all detections equally.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Survive Low Precision with Efficient Prioritization
&lt;/h2&gt;

&lt;p&gt;A tool with &lt;strong&gt;poor precision&lt;/strong&gt; can still be effective if it has &lt;strong&gt;strong recall&lt;/strong&gt; and the right &lt;strong&gt;prioritization strategies&lt;/strong&gt;. Security teams, especially in large enterprises, focus on &lt;strong&gt;high-impact incidents first&lt;/strong&gt;, ensuring critical secrets are addressed before lower-priority findings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Efficient Triage: Focusing on Critical Leaks
&lt;/h3&gt;

&lt;p&gt;Not all exposed secrets pose the same risk. A &lt;strong&gt;cloud provider API key&lt;/strong&gt; is far more dangerous than a test credential. &lt;strong&gt;Enterprises prioritize based on impact&lt;/strong&gt;, so a tool with high recall ensures critical leaks aren’t missed, even if some false positives need filtering.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prioritization Helps But Isn’t a Silver Bullet
&lt;/h3&gt;

&lt;p&gt;Unlike provider-specific secrets, &lt;strong&gt;generic secrets lack explicit identifiers&lt;/strong&gt;, making them harder to classify automatically. Prioritization algorithms will not prioritize them correctly.&lt;/p&gt;

&lt;p&gt;Moreover, even with prioritization, poor precision leads to alert fatigue. The best defense is &lt;strong&gt;high recall with strong precision&lt;/strong&gt;—catching real secrets while minimizing noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking for the Best Solution
&lt;/h2&gt;

&lt;p&gt;Choosing the best secrets detection tool isn’t just about finding the one with the highest recall or the fewest false positives—it’s about &lt;strong&gt;balancing precision and recall&lt;/strong&gt; to ensure real-world effectiveness. The &lt;strong&gt;F1 score&lt;/strong&gt; is the most reliable way to compare solutions fairly, as it accounts for both factors. The ideal solution finds the &lt;strong&gt;right trade-off&lt;/strong&gt;, maximizing true positives while minimizing noise. However, it's important to remember that even a single missed secret can lead to a breach, so solutions with poor recall should be approached with caution.&lt;/p&gt;

&lt;p&gt;It's also important to &lt;strong&gt;consider the context&lt;/strong&gt;. Organizations should tailor their evaluation based on the types of secrets that pose the highest risk to them. A solution that performs well on highly critical secrets, such as cloud access keys or authentication tokens, will provide more security than one that simply maximizes detections without differentiation.&lt;/p&gt;

&lt;p&gt;Finally, beware of tools that claim near &lt;strong&gt;100% recall&lt;/strong&gt;—such a claim &lt;a href="https://blog.gitguardian.com/should-we-target-zero-false-positives/" rel="noopener noreferrer"&gt;likely indicates a lack of real-world constraints&lt;/a&gt;. This would mean the solution is not designed to be as efficient with actual data. The best secrets detection solution isn’t the one that flags the most findings; it’s the one that helps your security team take the right actions.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>security</category>
    </item>
    <item>
      <title>From False Positives to Potential Breaches: The Risks of Prematurely Closing Incidents</title>
      <dc:creator>Ferdinand Boas</dc:creator>
      <pubDate>Sun, 20 Oct 2024 17:36:33 +0000</pubDate>
      <link>https://dev.to/gitguardian/from-false-positives-to-potential-breaches-the-risks-of-prematurely-closing-incidents-hjo</link>
      <guid>https://dev.to/gitguardian/from-false-positives-to-potential-breaches-the-risks-of-prematurely-closing-incidents-hjo</guid>
      <description>&lt;p&gt;In the fast-paced world of software development, efficiently managing security incidents is crucial for maintaining a robust security posture. Automated secrets detection solutions are pivotal in identifying and alerting teams to exposed secrets within code repositories. &lt;strong&gt;Deciding whether to resolve or ignore these incidents can be challenging&lt;/strong&gt;. Ignoring an incident is reasonable when a secret is low-risk or a false positive. However, without careful consideration, &lt;strong&gt;this action can introduce significant risks, potentially leaving the organization vulnerable to security breaches&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In 2024, a publicly listed technology company experienced a breach when a thief stole an employee's developer's Access Token. It gave them access to a codebase containing an active credential that had been incorrectly labeled as a test credential. Thankfully, the damage was minimal: a forensic investigation revealed that the intruder only accessed a small portion of the production environment and obtained personal information on a limited number of individuals and dummy data.&lt;/p&gt;

&lt;p&gt;In this blog post, we'll explore the risks of ignoring an incident and discuss best practices to ensure such decisions do not compromise your organization's security.&lt;/p&gt;

&lt;h2&gt;
  
  
  The risks associated with ignoring an incident and premature closure
&lt;/h2&gt;

&lt;p&gt;Ignoring a secret typically occurs when the identified secret poses low/no significant security risk. Common reasons to ignore a secret include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The secret was used for testing purposes&lt;/strong&gt;: During development or testing phases, developers might use dummy or placeholder secrets to simulate real-world scenarios. These secrets are not meant to be used in production environments and are often considered harmless.&lt;br&gt;
&lt;strong&gt;The rationale for ignoring&lt;/strong&gt;: Since these secrets are not tied to sensitive data or production systems, they might be deemed safe to ignore.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The secret is a false positive&lt;/strong&gt;: GitGuardian may sometimes flag non-sensitive information as a secret due to pattern-matching algorithms. For instance, a string that looks like an API key or a password might actually be something entirely benign, like a configuration identifier or a comment in the code.&lt;br&gt;
&lt;strong&gt;The rationale for ignoring&lt;/strong&gt;: False positives can be ignored to reduce noise and focus on real security threats.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The secret has a low risk&lt;/strong&gt;: Not all secrets are created equal; some may have a low impact if exposed. For example, a secret might only grant access to a non-critical service or a sandbox environment with minimal risk to the organization.&lt;br&gt;
&lt;strong&gt;The rationale for ignoring&lt;/strong&gt;: If a secret's potential impact is determined to be negligible, it might be ignored to focus resources on more critical issues.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even diligent teams can sometimes ignore or prematurely dismiss security alerts. Understanding and addressing the causes is crucial for improving incident management.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alert fatigue&lt;/strong&gt;: GitGuardian may be just one of the numerous security tools security teams utilize. Some of these tools raise &lt;strong&gt;an overwhelming number of alerts&lt;/strong&gt;, leading to desensitization and the potential to overlook real threats.&lt;br&gt;
For instance, in 2017, &lt;a href="https://en.wikipedia.org/wiki/2017_Equifax_data_breach" rel="noopener noreferrer"&gt;Equifax overlooked some security alerts&lt;/a&gt;, resulting in the breach of around 150 million private records. After litigation, the total cost of the settlement exceeded $500 million.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lack of training&lt;/strong&gt;: Inadequate training or awareness among team members contributes to &lt;strong&gt;misclassifying incidents as low-risk or false positives&lt;/strong&gt;.&lt;br&gt;
All teams don't learn the same way; that's why GitGuardian provides continuous education and training on how to reduce these errors &lt;a href="https://blog.gitguardian.com/developer-education-gitguardian-supports-your-learning-style/" rel="noopener noreferrer"&gt;through different media&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resource constraints&lt;/strong&gt;: Limited resources—such as time, personnel, and budget—can lead to shortcuts in incident management and premature closure of incidents.&lt;br&gt;
When dealing with a pile of security debts, organizations can adopt &lt;a href="https://blog.gitguardian.com/a-practical-guide-to-prioritize-and-remediate-thousands-of-secrets-leaks-incidents/" rel="noopener noreferrer"&gt;strategies to manage these constraints without compromising security&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Human bias&lt;/strong&gt;: Cognitive biases, such as confirmation bias or optimism bias, might cause security teams to &lt;strong&gt;downplay certain alerts&lt;/strong&gt;. For example, if a team member has encountered similar alerts that turned out to be false positives in the past, they might prematurely dismiss a legitimate threat.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Out of about 300,000 ignored incidents by GitGuardian users, &lt;strong&gt;the secret was still valid in 4.7% of cases&lt;/strong&gt;. When taking into account all 650,000 closed incidents, &lt;strong&gt;this number increases to 6%&lt;/strong&gt;. This means &lt;strong&gt;tens of thousands of closed incidents could be potential vulnerabilities&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Ignoring a security incident without thorough consideration can pose significant risks.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Overlooking emerging threats&lt;/strong&gt;: Cybersecurity threats are constantly evolving, and what might seem like a low-risk or false positive today could be &lt;strong&gt;exploited by attackers in new and unforeseen ways&lt;/strong&gt;. Teams might assume all problems are under control, with the risk of deploying compromised code into a production environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inconsistent security practices&lt;/strong&gt;: Regularly ignoring secrets without a standardized review process can lead to inconsistent security practices across the organization. Different &lt;strong&gt;team members might have varying thresholds&lt;/strong&gt; for what constitutes a low-risk secret. This inconsistency can create gaps in security defenses, where some risks are ignored without proper justification.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compliance and audit challenges&lt;/strong&gt;: Security audit trails are essential for maintaining an accurate historical record of security incidents and their resolutions. Failing to demonstrate thorough review and justification for ignored incidents can result in &lt;strong&gt;failed audits, regulatory fines&lt;/strong&gt;, the inability to trace the root cause of a security breach, or the need for costly remediation efforts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Missed opportunities for security improvement&lt;/strong&gt;: By ignoring incidents, organizations may miss the chance to &lt;strong&gt;refine their security processes, update playbooks&lt;/strong&gt;, or adjust scanning tools to reduce false positives, ultimately weakening overall security resilience.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Organizations should establish clear guidelines and best practices for incident review to mitigate these risks and ensure that all security incidents are carefully evaluated before being ignored or closed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigating incident closure in GitGuardian
&lt;/h2&gt;

&lt;p&gt;Properly resolving incidents strengthens security, while incorrectly ignoring or closing them can compromise the organization's defenses. Therefore, it is crucial to comprehend the context and consequences of each action &lt;strong&gt;to maintain a robust security posture&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.gitguardian.com/secrets-detection/remediate/remediate-incidents" rel="noopener noreferrer"&gt;When remediating an incident in GitGuardian&lt;/a&gt;, there are two ways to close an open incident:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resolve&lt;/strong&gt;: When an incident is resolved in GitGuardian, it means that the underlying security issue has been addressed, and &lt;strong&gt;the risk has been mitigated&lt;/strong&gt;. This usually occurs when an exposed secret is rotated. The resolution typically involves verifying that the issue no longer poses a threat and documenting the steps taken to fix it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignore&lt;/strong&gt;: Ignoring an incident is appropriate when the identified issue is a known false positive or does not pose a significant security risk.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Users may mistakenly close incidents. That's why &lt;strong&gt;GitGuardian sends notifications to their team managers&lt;/strong&gt; (or the workspace manager if the user who closed the incident is not part of a team) when an incident is ignored while the secret is still valid.&lt;br&gt;
These notifications are meant to encourage &lt;strong&gt;further review and prevent incidents from being overlooked&lt;/strong&gt;, providing an extra level of protection against premature closures.&lt;/p&gt;

&lt;p&gt;Furthermore, you can &lt;strong&gt;audit closed incidents&lt;/strong&gt; using &lt;a href="https://docs.gitguardian.com/platform/collaboration-and-sharing/saved-views" rel="noopener noreferrer"&gt;Saved views&lt;/a&gt;: one of the default incident views is dedicated to closed incidents with valid secrets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13xaniz62i6tctvq1oup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13xaniz62i6tctvq1oup.png" alt="Saved views" width="800" height="597"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What is true today may not be true tomorrow. Some services, such as AWS, may allow users to temporarily deactivate secrets.&lt;br&gt;
&lt;strong&gt;When a secret has been exposed, GitGuardian will continue to monitor its validity, even if the incident is closed&lt;/strong&gt;. An invalid secret may become valid later. If this occurs, GitGuardian will create new incidents to &lt;strong&gt;avoid unpleasant surprises&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best practices for closing incidents in GitGuardian
&lt;/h2&gt;

&lt;p&gt;GitGuardian is convinced that detection without remediation is just noise and want to help you &lt;a href="https://blog.gitguardian.com/coordinating-and-prioritizing-remediation-with-gitguardian/" rel="noopener noreferrer"&gt;close incidents more efficiently&lt;/a&gt;. More specifically, you should follow these guidelines before ignoring or closing any incident:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verify the incident's nature&lt;/strong&gt;: Confirm whether the incident is a real threat, a test, or a false positive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use playbooks&lt;/strong&gt;: Consider employing a playbook to standardize the process of managing and closing incidents. GitGuardian offers a default remediation playbook and the option to create custom playbooks. A well-defined playbook ensures that all necessary steps are taken, &lt;strong&gt;reducing the risk of human error&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Team collaboration: Encourage collaboration with team members and security experts when unsure how to handle an incident. Leveraging the collective knowledge of the team can lead to &lt;strong&gt;better decision-making and prevent oversights&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Assess the risk&lt;/strong&gt;: Evaluate the potential impact of the incident on the organization's security.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Implement remediatio&lt;/strong&gt;n: If the incident is valid, take the necessary steps to remediate the issue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep detailed records&lt;/strong&gt;: Thoroughly document the reason for closing or ignoring an incident. This documentation is crucial for &lt;strong&gt;future reference&lt;/strong&gt;, especially during audits or similar incidents. Additionally, other team members can comprehend the rationale behind the closure if they revisit the incident later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Double-check&lt;/strong&gt;: Before closing any incident, verify the issue has been fully resolved or correctly classified. It means confirming that exposed secrets have been rotated or ensuring that the incident's root cause has been addressed.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While some secrets may be safely ignored, this decision should never be made lightly. &lt;strong&gt;The potential risks of ignoring genuine security threats far outweigh the convenience of reducing alert noise&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;By following best practices, you can balance efficiency and security, ensuring your codebase remains well-protected against current and emerging threats.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>security</category>
    </item>
    <item>
      <title>How I almost won an NLP competition without knowing any Machine Learning</title>
      <dc:creator>Ferdinand Boas</dc:creator>
      <pubDate>Tue, 10 Aug 2021 13:51:28 +0000</pubDate>
      <link>https://dev.to/ferdi05/how-i-almost-won-an-nlp-competition-without-knowing-any-machine-learning-24la</link>
      <guid>https://dev.to/ferdi05/how-i-almost-won-an-nlp-competition-without-knowing-any-machine-learning-24la</guid>
      <description>&lt;p&gt;One of the cool things about Machine Learning is that you can see it as a competition. Your models can be evaluated with many performance indicators, and be ranked on various leaderboards. You can compete against other Machine Learning practitioners around the world, and your competitors can be a student in Malaysia or the largest AI lab at Stanford University.&lt;br&gt;
&lt;a href="https://www.kaggle.com/" rel="noopener noreferrer"&gt;Kaggle&lt;/a&gt; started as a platform to host such Machine Learning contests, and it gained a lot of attention from the data science community. The best data scientists exhibit on Kaggle their most sophisticated Machine Learning skills, craft the most elaborated models to reign over these competitions.&lt;br&gt;
Kaggle is now a broader platform, where you can enter these competitions but also learn data science, discuss it, and collaborate with fellow data scientists.&lt;/p&gt;

&lt;p&gt;Most of the Kaggle competitors are Machine Learning practitioners. Many software engineers do not enter these competitions, mostly because &lt;em&gt;they think&lt;/em&gt; that they do not have the needed skill set, tools, or time to be successful in them.&lt;/p&gt;

&lt;p&gt;Machine Learning can be hard to learn and use. It’s a very technical field.&lt;br&gt;
Running a Machine Learning project is complex: you will have to gather and clean data, choose a pre-trained model or train a model that suits your needs, fine-tune it for your curated dataset, and deploy the model in a production environment. You will also need to worry about monitoring, scalability, latency, reliability...&lt;br&gt;
This is usually a resource-intensive process, it takes time, knowledge, compute resources, and money. &lt;em&gt;This does not fit well with the regular activities of a software engineer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At this stage, I need to point out that I am not a data scientist. &lt;br&gt;
&lt;strong&gt;You may now wonder how I ranked among the best data scientists in a Kaggle Natural Language Processing (NLP) challenge without using any Machine Learning.&lt;/strong&gt;&lt;br&gt;
This blog post explains how I successively leveraged &lt;a href="https://huggingface.co/" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt; 🤗 AutoNLP web interface and 🤗 Inference API to achieve this result.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find all the scripts and assets used in this GitHub repository.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ferdi05" rel="noopener noreferrer"&gt;
        ferdi05
      &lt;/a&gt; / &lt;a href="https://github.com/ferdi05/kaggle-disaster-tweet-competition" rel="noopener noreferrer"&gt;
        kaggle-disaster-tweet-competition
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Participating to a Kaggle competition without coding any Machine Learning
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  The Kaggle competition
&lt;/h2&gt;

&lt;p&gt;Entering a Kaggle competition is straightforward. You are asked to perform a task such as &lt;a href="https://en.wikipedia.org/wiki/Sentiment_analysis" rel="noopener noreferrer"&gt;sentiment analysis&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Object_detection" rel="noopener noreferrer"&gt;object detection&lt;/a&gt; that can be solved with Machine Learning. Kaggle provides a  training dataset with examples of the task to achieve. You can use this dataset to train a Machine Learning model. Then you can use this model to perform the same task on a test dataset (also provided by Kaggle). This is your attempt at solving the challenge. Then you will submit your model predictions for this test dataset to Kaggle and they will evaluate it and give you a ranking in the competition that you entered.&lt;/p&gt;

&lt;p&gt;You will find plenty of NLP competitions on the Kaggle website. I participated in the &lt;a href="https://www.kaggle.com/c/nlp-getting-started/" rel="noopener noreferrer"&gt;Natural Language Processing with Disaster Tweets&lt;/a&gt; competition as it is quite recent (7 months when writing this post) and has over 3,000 submissions from other teams.&lt;br&gt;
This competition challenged me to build a Machine Learning model that predicts if a tweet is about a real disaster or not.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-629010812728963072-339" src="https://platform.twitter.com/embed/Tweet.html?id=629010812728963072"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-629010812728963072-339');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=629010812728963072&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This tweet is not about a real disaster&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Kaggle provides a &lt;a href="https://github.com/ferdi05/kaggle-disaster-tweet-competition/blob/main/assets/train_original.csv" rel="noopener noreferrer"&gt;training&lt;/a&gt; dataset of around 7,500 tweets (the input object) with their associated label (the desired output value). These labels tell if each tweet is about a disaster (its label is 1) or not (its label is 0). This dataset will be used to train a few Machine Learning models and evaluate them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9hjmvhmrdqlsuvhcaip.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9hjmvhmrdqlsuvhcaip.png" alt="Kaggle training dataset" width="800" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kaggle also provides a &lt;a href="https://github.com/ferdi05/kaggle-disaster-tweet-competition/blob/main/assets/test.csv" rel="noopener noreferrer"&gt;test&lt;/a&gt; dataset of around 3,200+ tweets without any paired label. We will use the newly created Machine Learning model to predict if they are about a disaster, asking the Machine Learning model to apply labels to each of these tweets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0lb2urf4q5ppiiqk6nm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0lb2urf4q5ppiiqk6nm.png" alt="Kaggle test dataset" width="800" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both datasets also contain two other data columns that will not be used: a keyword and the location of the tweet.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤗 AutoNLP web interface to the rescue
&lt;/h2&gt;

&lt;p&gt;The process of training a Machine Learning model is not straightforward. It requires collecting cleaning and formatting data, selecting a Machine Learning algorithm, playing with the algorithm parameters, training the model, evaluating its performance, and iterating. And this does not guarantee that performances will reach your expectations.&lt;br&gt;
This is a resource-intensive process. Fortunately, I used a web interface to do all the heavy-lifting and save hours of Machine Learning-induced head-scratching.&lt;/p&gt;
&lt;h3&gt;
  
  
  What is 🤗 AutoNLP?
&lt;/h3&gt;

&lt;p&gt;Leveraging its experience with the most performant architectures of NLP, Hugging Face offers the &lt;a href="https://ui.autonlp.huggingface.co/" rel="noopener noreferrer"&gt;🤗 AutoNLP web interface&lt;/a&gt; to automatically train, evaluate and deploy state-of-the-art NLP models for different tasks. All you need to do is feed it your datasets.&lt;/p&gt;

&lt;p&gt;🤗 AutoNLP uses &lt;a href="https://machinelearningmastery.com/supervised-and-unsupervised-machine-learning-algorithms/" rel="noopener noreferrer"&gt;supervised learning algorithms&lt;/a&gt; to train the candidate Machine Learning models. This means that these models will try to reproduce what they learned from examples that pair an input object and its desired output value. After their training, these models should successfully pair unseen input objects with their correct output values.&lt;/p&gt;

&lt;p&gt;🤗 AutoNLP will train a range of NLP models suitable for the task required by the competition and will use a various set of configurations for each of them. Then each model’s performance will be automatically evaluated. I saved a lot of resources and money by avoiding their computer-intensive training.&lt;br&gt;
Later I selected the most performant model to make predictions for the Kaggle competition.&lt;/p&gt;
&lt;h3&gt;
  
  
  Training Machine Learning models with data only
&lt;/h3&gt;

&lt;p&gt;The competition requires to label each tweet as related to a disaster or not. And binary text classification is one of the tasks achievable with the 🤗 AutoNLP web interface. So I started a new project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7rgczuy00q3v18k2m85.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7rgczuy00q3v18k2m85.png" alt="New AutoNLP project" width="600" height="724"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this competition, Kaggle provides only one training dataset but you need one dataset to train the models (the training dataset) and another one (the validation dataset) to evaluate their performance.&lt;br&gt;
I split the original dataset provided by Kaggle into 2 datasets using a rule of thumb ratio of 80%-20%.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7a2t14e2u2mn4w3tzrbg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7a2t14e2u2mn4w3tzrbg.png" alt="Column mapping" width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The columns of both datasets need to be mapped. The &lt;em&gt;text&lt;/em&gt; column is the input object and the &lt;em&gt;target&lt;/em&gt; column is the desired output value. Here the input object is the tweet content, and the output value is its associated label.&lt;/p&gt;

&lt;p&gt;Then the web interface started the training and did its magic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd30g1p6jgyvrpv3c7bx7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd30g1p6jgyvrpv3c7bx7.gif" alt="training models with AutoNLP" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a few minutes, models were trained, evaluated, and uploaded on the &lt;a href="https://huggingface.co/models" rel="noopener noreferrer"&gt;Hugging Face Hub&lt;/a&gt; (with private visibility). They were ready to serve, still without any Machine Learning instructions, as you will see later.&lt;/p&gt;

&lt;p&gt;For this competition, Kaggle evaluates the performance of the predictions with their &lt;a href="https://en.wikipedia.org/wiki/F-score" rel="noopener noreferrer"&gt;F1 score&lt;/a&gt;. This is an accuracy metric for a machine learning model. So the best model was the one with the highest F1 score.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flklovjp5gkspu5zosrxy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flklovjp5gkspu5zosrxy.png" alt="Model metrics" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kaggle sometimes evaluates results with more sophisticated metrics. Conveniently 🤗 AutoNLP web interface automatically uploads every trained model’s file on the Hugging Face Hub with &lt;a href="https://huggingface.co/ferdinand/autonlp-kaggle-competition-6381329" rel="noopener noreferrer"&gt;their associated card&lt;/a&gt;. Each card includes the model metrics (that you may combine according to your need) and code snippets to use the model. And there is even a widget to quickly experiment with the model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjiw0mde4zsxvnw62y95v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjiw0mde4zsxvnw62y95v.png" alt="Model card" width="800" height="591"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Solving the Kaggle challenge with the 🤗 Inference API
&lt;/h2&gt;

&lt;p&gt;It is now time to use the most performant model on the test dataset provided by Kaggle.&lt;br&gt;
There are two different ways to use the model:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the data scientist way: deploying the model on a dedicated infrastructure, or on a Machine Learning platform&lt;/li&gt;
&lt;li&gt;the &lt;em&gt;developer-friendly&lt;/em&gt; way of using it: through API calls. This is the one that I will describe here.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Serving Machine Learning models with the 🤗 Inference API
&lt;/h3&gt;

&lt;p&gt;Using Machine Learning models in production is hard, even for Machine Learning engineers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you may have a difficult time handling large and complex models&lt;/li&gt;
&lt;li&gt;your tech architecture can be unoptimized&lt;/li&gt;
&lt;li&gt;your hardware may not meet your requirements
Your model may not have the scalability, reliability or speed performances that you were expecting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I relied on the &lt;a href="https://huggingface.co/inference-api" rel="noopener noreferrer"&gt;🤗 Inference API&lt;/a&gt; to use my model, still without coding any Machine Learning. The API allows to reach up to &lt;a href="https://huggingface.co/blog/accelerated-inference" rel="noopener noreferrer"&gt;100x speedup&lt;/a&gt; compared to deploying my model locally or on a cloud, thanks to many optimization techniques. And the API has built-in scalability which makes it a perfect addition to a software production workflow, while controlling the costs as I will not need any extra infrastructure resources.&lt;/p&gt;
&lt;h3&gt;
  
  
  A few API calls to solve the challenge
&lt;/h3&gt;

&lt;p&gt;Let’s call the 🤗 Inference API for each row of the test dataset, and write the output value in the submission file.&lt;br&gt;
I could have used the API via regular HTTP calls, but there is an alternate way: the &lt;a href="https://github.com/huggingface/huggingface_hub/" rel="noopener noreferrer"&gt;huggingface_hub library&lt;/a&gt; conveniently offers a wrapper client to handle these requests, and I used it to call the API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;huggingface_hub.inference_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InferenceApi&lt;/span&gt;

&lt;span class="n"&gt;inference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InferenceApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ferdinand/autonlp-kaggle-competition-6381329&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;API_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# URL of our model with our API token
&lt;/span&gt;&lt;span class="n"&gt;MODEL_MAX_LENGTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt; &lt;span class="c1"&gt;# parameter of our model, can be seen in config.json at "max_position_embeddings"
&lt;/span&gt;
&lt;span class="n"&gt;fr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assets/test.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Kaggle test data
&lt;/span&gt;&lt;span class="n"&gt;csv_read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# skipping the header row
&lt;/span&gt;
&lt;span class="n"&gt;fw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assets/submission.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UTF8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# our predictions data
&lt;/span&gt;&lt;span class="n"&gt;csv_write&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;csv_write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;target&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;# writing the header row
&lt;/span&gt;
&lt;span class="c1"&gt;#returns a label : about a disaster or not given a tweet content
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tweet_content&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

   &lt;span class="c1"&gt;# calling the API, payload is the tweet content , possibly truncated to meet our model requirements
&lt;/span&gt;   &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tweet_content&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;MODEL_MAX_LENGTH&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

   &lt;span class="c1"&gt;# Determining which label to return according to the prediction with the highest score
&lt;/span&gt;   &lt;span class="c1"&gt;# example of an API call response: [[{'label': '0', 'score': 0.9159180521965027}, {'label': '1', 'score': 0.08408192545175552}]]
&lt;/span&gt;   &lt;span class="n"&gt;max_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
   &lt;span class="n"&gt;max_label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;dic&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
       &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dic&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
           &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dic&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
           &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max_score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
               &lt;span class="n"&gt;max_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
               &lt;span class="n"&gt;max_label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;max_label&lt;/span&gt;


&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;csv_read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# call the API for each row
&lt;/span&gt;
   &lt;span class="c1"&gt;# writing in the submission file the tweet ID and its associated label: about a disaster or not
&lt;/span&gt;   &lt;span class="n"&gt;write_row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])]&lt;/span&gt; &lt;span class="c1"&gt;# row[0] is the tweet ID, row[3] is the tweet content
&lt;/span&gt;   &lt;span class="n"&gt;csv_write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;write_row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running the 🤗 Inference API on all the input data (it may take a while), I ended up with a file that I submitted to Kaggle for evaluation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw4wlvodvndhx0evy3kqu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw4wlvodvndhx0evy3kqu.png" alt="Kaggle evaluation" width="800" height="71"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This model made it to the top 15% of the competitors with a 0.83 mean score!&lt;br&gt;
At first, I was surprised to not rank higher. Unfortunately, the test dataset and its associated label used for this competition are available publicly. So a few clever contestants submitted it and received an approximate 1.00 score, which is not something realistic in a data science problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5w3nf4fvr6qgxkskm0kw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5w3nf4fvr6qgxkskm0kw.png" alt="Kaggle leaderboard" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having a second look at the leaderboard, I saw that the best data science teams have a 0.85 score. This is very close to the score that I obtained, and another 🤗AutoNLP test may give better results, depending on how lucky I am with the random variations of each model’s parameters. Given the time and resources invested in solving this challenge, this is almost a win!&lt;/p&gt;

&lt;h2&gt;
  
  
  Do more with the AutoNLP Python package
&lt;/h2&gt;

&lt;p&gt;With the 🤗 AutoNLP web interface, the 🤗 Inference API, and a very few lines of code, NLP models were automatically created, deployed, and used to achieve a great ranking in an NLP competition without learning or using any Machine Learning techniques.&lt;/p&gt;

&lt;p&gt;🤗 AutoNLP can also be used as a Python package and can support more Machine Learning tasks than those provided by the web interface - but the interface is quickly catching up. You can use the package to perform tasks like speech recognition and enter even more Kaggle competitions!&lt;/p&gt;

&lt;p&gt;If you want to win a Kaggle competition or to train a model for your business or pleasure, you can get started with AutoNLP &lt;a href="https://huggingface.co/autonlp" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nlp</category>
      <category>kaggle</category>
      <category>datascience</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
