6

我们有一个 C2C 网站,我们不鼓励在我们的网站上销售品牌产品。我们已经建立了一个包含NikeD&G等品牌词的数据库,并制作了一种算法,可以过滤这些词的产品信息,如果包含这些词,则禁用产品。

我们当前的算法从提供的文本中删除所有空格和特殊字符,并将文本与数据库中的单词匹配。这些情况需要被算法捕获并被有效捕获:

  • 我是耐克世界
  • 我有耐克鞋
  • 我有耐克鞋
  • 我卖手机壳
  • 我卖 iphone 外壳
  • 你可以有iphone

现在的问题是它还捕获了以下问题:

  • rapid服装厂(D&G)
  • rosNIK Electronics(耐克)

可以做些什么来防止这种错误匹配,同时保持捕捉真实案例的效率?

编辑

下面是那些更了解代码的人的代码:

$orignal_txt = preg_replace('/&.{0,}?;/', '', (strip_tags($orignal_txt)));
$orignal_txt_nospace = preg_replace('/\W/', '', $orignal_txt);
{
    $qry_kws = array("nike", "iphone", "d&g");
    foreach($qry_kws as $rs_kw)
    {       
        $no_space_db_kw = preg_replace('/\W/', '', $rs_kw);
        if(stristr($orignal_txt_nospace, $rs_kw))
        {
            $ipr_banned_keywords[] = strtolower($rs_kw);
        }
        else if(stristr($orignal_txt_nospace, $no_space_db_kw))
        {
                $ipr_banned_keywords[] = strtolower($rs_kw);
        }

    }
}
4

4 回答 4

5

只是在玩......(不要在生产中使用)

$data = array(
        "i am nike world",
        "i have n ikee shoes",
        "i have nikeeshoes",
        "i sell i-phone casings",
        "i sell iphone-casings",
        "you can have iphone",
        "rapiD Garment factor",
        "rosNIK Electronics",
        "Buy you self N I K E",
        "B*U*Y I*P*H*O*N*E BABY",
        "My Phone Is not available");


$ban = array("nike","d&g","iphone");

示例 1:

$filter = new BrandFilterIterator($data);
$filter->parseBan($ban);
foreach ( $filter as $word ) {
    echo $word, PHP_EOL;
}

输出 1

rapiD Garment factor
rosNIK Electronics
My Phone Is not available

示例 2

$filter = new BrandFilterIterator($data,true); //reverse filter
$filter->parseBan($ban);
foreach ( $filter as $word ) {
    echo $word, " " , json_encode($word->getBan()) ,  PHP_EOL;
}

输出 2

i am nike world ["nike"]
i have n ikee shoes ["nike"]
i have nikeeshoes ["nike"]
i sell i-phone casings ["iphone"]
i sell iphone-casings ["iphone"]
you can have iphone ["iphone"]
Buy you self N I K E ["nike"]
B*U*Y I*P*H*O*N*E BABY ["iphone"]

使用的类

class BrandFilterIterator extends FilterIterator {
    private $words = array();
    private $reverse = false;

    function __construct(array $words, $reverse = false) {
        $this->reverse = $reverse;
        foreach ( $words as $word ) {
            $this->words[] = new Word($word);
        }
        parent::__construct(new ArrayIterator($this->words));
    }

    function parseBan(array $ban) {
        foreach ( $ban as $item ) {
            foreach ( $this->words as $word ) {
                $word->checkMetrix($item);
            }
        }
    }

    public function accept() {
        if ($this->reverse) {
            return $this->getInnerIterator()->current()->accept() ? false : true;
        }
        return $this->getInnerIterator()->current()->accept();
    }
}


class Word {
    private $ban = array();
    private $word;
    private $parts;
    private $accept = true;

    function __construct($word) {
        $this->word = $word;
        $this->parts = explode(" ", $word);
    }

    function __toString() {
        return $this->word;
    }

    function getTrim() {
        return preg_replace('/\W/', '', $this->word);
    }

    function accept() {
        return $this->accept;
    }

    function getBan() {
        return array_unique($this->ban);
    }

    function reject($ban = null) {
        $ban === null or $this->ban[] = $ban;
        $this->accept = false;
        return $this->accept;
    }

    function checkMetrix($ban) {
        foreach ( $this->parts as $part ) {
            $part = strtolower($part);
            $ban = strtolower($ban);
            $t = ceil(strlen(strtolower($ban)) / strlen($part) * 100);
            $s = similar_text($part, $ban, $p);
            $l = levenshtein($part, $part);
            if (ceil($p) >= $t || ($t == 100 && $p >= 75 && $l == 0)) {
                $this->reject($ban);
            }
        }
        // Detect Bad Use of space
        if (ceil(strlen($this->getTrim()) / strlen($this->word) * 100) < 75) {
            if (stripos($this->getTrim(), $ban) !== false) {
                $this->reject($ban);
            }
        }
        return $this->accept;
    }
}
于 2012-12-21T12:03:05.503 回答
3

Simple, do the brand match before you remove spaces/special characters. Then it won't match these weird edge cases.

于 2012-12-21T09:39:28.433 回答
3

您已经知道这一点,但值得明确指出:您当前的算法完全不适合这项任务。它甚至无法处理简单的情况,更不用说人们故意试图通过您的过滤器的情况。你只能用你当前的过滤器做一件事,那就是把它完全扔掉——它不能工作。

虽然我们在这里不讨论猥亵过滤器,但它几乎是同一类概念,因此建议您阅读一些由猥亵过滤器犯下的最严重错误。

这些文章主要处理误报——即过滤器对不应该匹配的内容进行匹配,从而阻止合法条目。这种事情可能会非常具有破坏性,因为它会使您的客户感到不安,并且如果这种情况经常发生,它将使人们远离您的网站。自然语言的复杂性使其几乎不可避免。

您还需要注意假阴性。这些是您的过滤器无法拾取它应该拾取的东西的地方。您的问题是垃圾邮件发送者拥有大量通过过滤器的技术。您当前的过滤器很容易通过,但即使是最先进的过滤器也可以被击败 - 检查您的收件箱中有多少垃圾邮件以证明这一点。而且他们一直在改变他们的技术,所以从长远来看,静态算法根本行不通。

贝叶斯过滤器似乎是您的最佳解决方案。这些过滤器可以随时学习。您需要密切关注它们并训练它们识别需要过滤的内容,因此设置起来会有些工作,但我怀疑您是否会以任何其他方式获得可行的解决方案。

于 2012-12-21T10:09:08.387 回答
1

这里只是一个想法。

你为什么不先进行匹配,如果它击中“品牌”过滤器,它会被放入审查队列中供你接受/拒绝,突出显示匹配以便于发现。

人类将能够几乎立即准确地发现品牌是否被使用。你甚至可以把它变成机器学习,谁知道呢:)

话虽如此,这不是正则表达式问题,也不能用漂亮的表达式来解决;系统需要训练,记住命中(增加信心)并从失误中学习。

于 2012-12-21T09:46:11.577 回答