コード例 #1
0
ファイル: app.py プロジェクト: mcookhome/pictophone
def game(name=None):
    ids= manager.getIDs()
    if 'username' in session:
        loggedin=True
        username=session['username']
        myGames=manager.getUserGames(username)
        if request.method=='POST':
            if request.form["submit"] == "Go":
                if manager.getProfilePath() != "profile/":
                    return redirect(manager.getProfilePath())
        if name is None:
            gamelist=manager.getCompleteGames()
            #print gamelist
            return render_template("game.html",loggedin=loggedin,username=username,ids=ids,gamelist=gamelist,myGames=myGames)
        print name
        gameFax=manager.getGameFax(name)
        starts=gameFax[0][2]
        ends=gameFax[-1][2]
        percent=match.match(starts,ends)
        percent=int(round(abs(percent)*100))
        
        finished=manager.isComplete(name)
        if finished is False:
            return render_template("game.html",loggedin=loggedin,username=username,ids=ids,reason="This game is still in progress!",myGames=myGames)
        else:
            return render_template("game.html", loggedin=loggedin, username=username,ids=ids,gameFax=gameFax,name=name,myGames=myGames,percent=percent)
    else:
        loggedin=False
        username = '******'
        return render_template("game.html", loggedin=loggedin, username=username,ids=ids)
コード例 #2
0
ファイル: app.py プロジェクト: mcookhome/pictophone
def game(name=None):
    ids = manager.getIDs()
    if 'username' in session:
        loggedin = True
        username = session['username']
        myGames = manager.getUserGames(username)
        if request.method == 'POST':
            if request.form["submit"] == "Go":
                if manager.getProfilePath() != "profile/":
                    return redirect(manager.getProfilePath())
        if name is None:
            gamelist = manager.getCompleteGames()
            #print gamelist
            return render_template("game.html",
                                   loggedin=loggedin,
                                   username=username,
                                   ids=ids,
                                   gamelist=gamelist,
                                   myGames=myGames)
        print name
        gameFax = manager.getGameFax(name)
        starts = gameFax[0][2]
        ends = gameFax[-1][2]
        percent = match.match(starts, ends)
        percent = int(round(abs(percent) * 100))

        finished = manager.isComplete(name)
        if finished is False:
            return render_template("game.html",
                                   loggedin=loggedin,
                                   username=username,
                                   ids=ids,
                                   reason="This game is still in progress!",
                                   myGames=myGames)
        else:
            return render_template("game.html",
                                   loggedin=loggedin,
                                   username=username,
                                   ids=ids,
                                   gameFax=gameFax,
                                   name=name,
                                   myGames=myGames,
                                   percent=percent)
    else:
        loggedin = False
        username = '******'
        return render_template("game.html",
                               loggedin=loggedin,
                               username=username,
                               ids=ids)
コード例 #3
0
    def forward(self, predictions, targets):
        """
        損失関数の計算。

        Parameters
        ----------
        predictions : SSD netの訓練時の出力(tuple)
            (loc=torch.Size([num_batch, 8732, 4]), conf=torch.Size([num_batch, 8732, 21]), dbox_list=torch.Size [8732,4])。

        targets : [num_batch, num_objs, 5]
            5は正解のアノテーション情報[xmin, ymin, xmax, ymax, label_ind]を示す

        Returns
        -------
        loss_l : テンソル
            locの損失の値
        loss_c : テンソル
            confの損失の値

        """

        # SSDモデルの出力がタプルになっているので、個々にばらす
        loc_data, conf_data, dbox_list = predictions

        # 要素数を把握
        num_batch = loc_data.size(0)  # ミニバッチのサイズ
        num_dbox = loc_data.size(1)  # DBoxの数 = 8732
        num_classes = conf_data.size(2)  # クラス数 = 21

        # 損失の計算に使用するものを格納する変数を作成
        # conf_t_label:各DBoxに一番近い正解のBBoxのラベルを格納させる
        # loc_t:各DBoxに一番近い正解のBBoxの位置情報を格納させる
        conf_t_label = torch.LongTensor(num_batch, num_dbox).to(self.device)
        loc_t = torch.Tensor(num_batch, num_dbox, 4).to(self.device)

        # loc_tとconf_t_labelに、
        # DBoxと正解アノテーションtargetsをmatchさせた結果を上書きする
        for idx in range(num_batch):  # ミニバッチでループ

            # 現在のミニバッチの正解アノテーションのBBoxとラベルを取得
            truths = targets[idx][:, :-1].to(self.device)  # BBox
            # ラベル [物体1のラベル, 物体2のラベル, …]
            labels = targets[idx][:, -1].to(self.device)

            # デフォルトボックスを新たな変数で用意
            dbox = dbox_list.to(self.device)

            # 関数matchを実行し、loc_tとconf_t_labelの内容を更新する
            # (詳細)
            # loc_t:各DBoxに一番近い正解のBBoxの位置情報が上書きされる
            # conf_t_label:各DBoxに一番近いBBoxのラベルが上書きされる
            # ただし、一番近いBBoxとのjaccard overlapが0.5より小さい場合は
            # 正解BBoxのラベルconf_t_labelは背景クラスの0とする
            variance = [0.1, 0.2]
            # このvarianceはDBoxからBBoxに補正計算する際に使用する式の係数です
            match(self.jaccard_thresh, truths, dbox, variance, labels, loc_t,
                  conf_t_label, idx)

        # ----------
        # 位置の損失:loss_lを計算
        # Smooth L1関数で損失を計算する。ただし、物体を発見したDBoxのオフセットのみを計算する
        # ----------
        # 物体を検出したBBoxを取り出すマスクを作成
        pos_mask = conf_t_label > 0  # torch.Size([num_batch, 8732])

        # pos_maskをloc_dataのサイズに変形
        pos_idx = pos_mask.unsqueeze(pos_mask.dim()).expand_as(loc_data)

        # Positive DBoxのloc_dataと、教師データloc_tを取得
        loc_p = loc_data[pos_idx].view(-1, 4)
        loc_t = loc_t[pos_idx].view(-1, 4)

        # 物体を発見したPositive DBoxのオフセット情報loc_tの損失(誤差)を計算
        loss_l = F.smooth_l1_loss(loc_p, loc_t, reduction='sum')

        # ----------
        # クラス予測の損失:loss_cを計算
        # 交差エントロピー誤差関数で損失を計算する。ただし、背景クラスが正解であるDBoxが圧倒的に多いので、
        # Hard Negative Miningを実施し、物体発見DBoxと背景クラスDBoxの比が1:3になるようにする。
        # そこで背景クラスDBoxと予想したもののうち、損失が小さいものは、クラス予測の損失から除く
        # ----------
        batch_conf = conf_data.view(-1, num_classes)

        # クラス予測の損失を関数を計算(reduction='none'にして、和をとらず、次元をつぶさない)
        loss_c = F.cross_entropy(batch_conf,
                                 conf_t_label.view(-1),
                                 reduction='none')

        # -----------------
        # これからNegative DBoxのうち、Hard Negative Miningで抽出するものを求めるマスクを作成します
        # -----------------

        # 物体発見したPositive DBoxの損失を0にする
        # (注意)物体はlabelが1以上になっている。ラベル0は背景。
        num_pos = pos_mask.long().sum(1, keepdim=True)  # ミニバッチごとの物体クラス予測の数
        loss_c = loss_c.view(num_batch, -1)  # torch.Size([num_batch, 8732])
        loss_c[pos_mask] = 0  # 物体を発見したDBoxは損失0とする

        # Hard Negative Miningを実施する
        # 各DBoxの損失の大きさloss_cの順位であるidx_rankを求める
        _, loss_idx = loss_c.sort(1, descending=True)
        _, idx_rank = loss_idx.sort(1)

        # (注釈)
        # 実装コードがかなり特殊で直感的ではないです。
        # 上記2行は、要は各DBoxに対して、損失の大きさが何番目なのかの情報を
        # 変数idx_rankとして高速に取得したいというコードです。
        #
        # DBOXの損失値の大きい方から降順に並べ、DBoxの降順のindexをloss_idxに格納。
        # 損失の大きさloss_cの順位であるidx_rankを求める。
        # ここで、
        # 降順になった配列indexであるloss_idxを、0から8732まで昇順に並べ直すためには、
        # 何番目のloss_idxのインデックスをとってきたら良いのかを示すのが、idx_rankである。
        # 例えば、
        # idx_rankの要素0番目 = idx_rank[0]を求めるには、loss_idxの値が0の要素、
        # つまりloss_idx[?}=0 の、?は何番かを求めることになる。ここで、? = idx_rank[0]である。
        # いま、loss_idx[?]=0の0は、元のloss_cの要素の0番目という意味である。
        # つまり?は、元のloss_cの要素0番目は、降順に並び替えられたloss_idxの何番目ですか
        # を求めていることになり、 結果、
        # ? = idx_rank[0] はloss_cの要素0番目が、降順の何番目かを示すことになる。

        # 背景のDBoxの数num_negを決める。HardNegative Miningにより、
        # 物体発見のDBoxの数num_posの3倍(self.negpos_ratio倍)とする。
        # ただし、万が一、DBoxの数を超える場合は、DBoxの数を上限とする
        num_neg = torch.clamp(num_pos * self.negpos_ratio, max=num_dbox)

        # idx_rankは各DBoxの損失の大きさが上から何番目なのかが入っている
        # 背景のDBoxの数num_negよりも、順位が低い(すなわち損失が大きい)DBoxを取るマスク作成
        # torch.Size([num_batch, 8732])
        neg_mask = idx_rank < (num_neg).expand_as(idx_rank)

        # -----------------
        # (終了)これからNegative DBoxのうち、Hard Negative Miningで抽出するものを求めるマスクを作成します
        # -----------------

        # マスクの形を整形し、conf_dataに合わせる
        # pos_idx_maskはPositive DBoxのconfを取り出すマスクです
        # neg_idx_maskはHard Negative Miningで抽出したNegative DBoxのconfを取り出すマスクです
        # pos_mask:torch.Size([num_batch, 8732])→pos_idx_mask:torch.Size([num_batch, 8732, 21])
        pos_idx_mask = pos_mask.unsqueeze(2).expand_as(conf_data)
        neg_idx_mask = neg_mask.unsqueeze(2).expand_as(conf_data)

        # conf_dataからposとnegだけを取り出してconf_hnmにする。形はtorch.Size([num_pos+num_neg, 21])
        conf_hnm = conf_data[(pos_idx_mask + neg_idx_mask).gt(0)].view(
            -1, num_classes)
        # (注釈)gtは greater than (>)の略称。これでmaskが1のindexを取り出す。
        # pos_idx_mask+neg_idx_maskは足し算だが、indexへのmaskをまとめているだけである。
        # つまり、posであろうがnegであろうが、マスクが1のものを足し算で一つのリストにし、それをgtで取得

        # 同様に教師データであるconf_t_labelからposとnegだけを取り出してconf_t_label_hnmに
        # 形はtorch.Size([pos+neg])になる
        conf_t_label_hnm = conf_t_label[(pos_mask + neg_mask).gt(0)]

        # confidenceの損失関数を計算(要素の合計=sumを求める)
        loss_c = F.cross_entropy(conf_hnm, conf_t_label_hnm, reduction='sum')

        # 物体を発見したBBoxの数N(全ミニバッチの合計)で損失を割り算
        N = num_pos.sum()
        loss_l /= N
        loss_c /= N

        return loss_l, loss_c
コード例 #4
0
    def forward(self, predictions, targets):
        """calculate loss function

        Args:
        - predictions: output of SSD net in training
            (loc=torch.Size([num_batch, 8732, 4]), conf=torch.Size([num_batch, 8732, 21]), dbox_list=torch.Size [8732,4])。
        - targets: [num_batch, num_objs, 5]

        Returns:
        - loss_l: loss of loc
        - loss_c : loss of conf
        """

        loc_data, conf_data, dbox_list = predictions

        num_batch = loc_data.size(0)  # size of mini-batch
        num_dbox = loc_data.size(1)  # number of DBox
        num_classes = conf_data.size(2)  # number of classes

        # label of the correct BBox closest to each DBox
        conf_t_label = torch.LongTensor(num_batch, num_dbox).to(self.device)
        # position of the correct BBox closest to each DBox
        loc_t = torch.Tensor(num_batch, num_dbox, 4).to(self.device)

        for idx in range(num_batch):
            truths = targets[idx][:, :-1].to(self.device)
            labels = targets[idx][:, -1].to(self.device)

            dbox = dbox_list.to(self.device)

            variance = [0.1, 0.2]
            match(self.jaccard_thresh, truths, dbox, variance, labels, loc_t,
                  conf_t_label, idx)

        # loss of position
        pos_mask = conf_t_label > 0  # torch.Size([num_batch, 8732])

        # transform pos_mask into size of loc_data
        pos_idx = pos_mask.unsqueeze(pos_mask.dim()).expand_as(loc_data)

        loc_p = loc_data[pos_idx].view(-1, 4)
        loc_t = loc_t[pos_idx].view(-1, 4)

        loss_l = F.smooth_l1_loss(loc_p, loc_t, reduction='sum')

        # loss of class prediction
        batch_conf = conf_data.view(-1, num_classes)

        loss_c = F.cross_entropy(batch_conf,
                                 conf_t_label.view(-1),
                                 reduction='none')

        num_pos = pos_mask.long().sum(1, keepdim=True)
        loss_c = loss_c.view(num_batch, -1)
        loss_c[pos_mask] = 0

        _, loss_idx = loss_c.sort(1, descending=True)
        _, idx_rank = loss_idx.sort(1)

        num_neg = torch.clamp(num_pos * self.negpos_ratio, max=num_dbox)

        neg_mask = idx_rank < (num_neg).expand_as(idx_rank)

        pos_idx_mask = pos_mask.unsqueeze(2).expand_as(conf_data)
        neg_idx_mask = neg_mask.unsqueeze(2).expand_as(conf_data)

        conf_hnm = conf_data[(pos_idx_mask + neg_idx_mask).gt(0)].view(
            -1, num_classes)
        conf_t_label_hnm = conf_t_label[(pos_mask + neg_mask).gt(0)]

        loss_c = F.cross_entropy(conf_hnm, conf_t_label_hnm, reduction='sum')

        N = num_pos.sum().float()
        loss_l /= N
        loss_c /= N

        return loss_l, loss_c
コード例 #5
0
    def test_match(self):
        self.maxDiff = None
        cases: List[MatchTestCase] = [
            {
                "name": "empty inputs",
                "scofo_output": [],
                "ref": [],
                "want": {
                    "miss_rate": 0.0,
                    "misalign_rate": 0.0,
                    "piece_completion": 0.0,
                    "std_of_error": 0.0,
                    "mean_absolute_error": 0.0,
                    "std_of_latency": 0.0,
                    "mean_latency": 0.0,
                    "std_of_offset": 0.0,
                    "mean_absolute_offset": 0.0,
                    "miss_num": 0,
                    "misalign_num": 0,
                    "total_num": 0,
                    "precision_rate": 1.0,
                },
            },
            {
                "name":
                "empty scofo_output",
                "scofo_output": [],
                "ref": [
                    {
                        "tru_time": 1,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "tru_time": 3,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                ],
                "want": {
                    "miss_rate": 1.0,
                    "misalign_rate": 0.0,
                    "piece_completion": 0.0,
                    "std_of_error": 0.0,
                    "mean_absolute_error": 0.0,
                    "std_of_latency": 0.0,
                    "mean_latency": 0.0,
                    "std_of_offset": 0.0,
                    "mean_absolute_offset": 0.0,
                    "miss_num": 2,
                    "misalign_num": 0,
                    "total_num": 2,
                    "precision_rate": 0.0,
                },
            },
            {
                "name":
                "empty ref",
                "scofo_output": [
                    {
                        "est_time": 12,
                        "det_time": 13,
                        "note_start": 14,
                        "midi_note_num": 42,
                    },
                    {
                        "est_time": 13,
                        "det_time": 14,
                        "note_start": 15,
                        "midi_note_num": 43,
                    },
                ],
                "ref": [],
                "want": {
                    "miss_rate": 0.0,
                    "misalign_rate": 0.0,
                    "piece_completion": 0.0,
                    "std_of_error": 0.0,
                    "mean_absolute_error": 0.0,
                    "std_of_latency": 0.0,
                    "mean_latency": 0.0,
                    "std_of_offset": 0.0,
                    "mean_absolute_offset": 0.0,
                    "miss_num": 0,
                    "misalign_num": 0,
                    "total_num": 0,
                    "precision_rate": 1.0,
                },
            },
            {
                "name":
                "missed all",
                "scofo_output": [
                    {
                        "est_time": 12,
                        "det_time": 13,
                        "note_start": 14,
                        "midi_note_num": 42,
                    },
                    {
                        "est_time": 13,
                        "det_time": 14,
                        "note_start": 15,
                        "midi_note_num": 43,
                    },
                ],
                "ref": [
                    {
                        "tru_time": 1,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "tru_time": 3,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                ],
                "want": {
                    "miss_rate": 1.0,
                    "misalign_rate": 0.0,
                    "piece_completion": 0.0,
                    "std_of_error": 0.0,
                    "mean_absolute_error": 0.0,
                    "std_of_latency": 0.0,
                    "mean_latency": 0.0,
                    "std_of_offset": 0.0,
                    "mean_absolute_offset": 0.0,
                    "miss_num": 2,
                    "misalign_num": 0,
                    "total_num": 2,
                    "precision_rate": 0.0,
                },
            },
            {
                "name":
                "misaligned all",
                "scofo_output": [
                    {
                        "est_time": 1000,
                        "det_time": 1000,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "est_time": 1000,
                        "det_time": 1000,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                ],
                "ref": [
                    {
                        "tru_time": 1,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "tru_time": 3,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                ],
                "want": {
                    "miss_rate": 0.0,
                    "misalign_rate": 1.0,
                    "piece_completion": 0.0,
                    "std_of_error": 0.0,
                    "mean_absolute_error": 0.0,
                    "std_of_latency": 0.0,
                    "mean_latency": 0.0,
                    "std_of_offset": 0.0,
                    "mean_absolute_offset": 0.0,
                    "miss_num": 0,
                    "misalign_num": 2,
                    "total_num": 2,
                    "precision_rate": 0.0,
                },
            },
            {
                "name":
                "aligned all",
                "scofo_output": [
                    {
                        "est_time": 1.1,
                        "det_time": 1.2,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "est_time": 3.1,
                        "det_time": 3.2,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                ],
                "ref": [
                    {
                        "tru_time": 1,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "tru_time": 3,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                ],
                "want": {
                    "miss_rate": 0.0,
                    "misalign_rate": 0.0,
                    "piece_completion": 1.0,
                    "std_of_error": 0.0,
                    "mean_absolute_error": 0.1,
                    "std_of_latency": 0.0,
                    "mean_latency": 0.1,
                    "std_of_offset": 0.0,
                    "mean_absolute_offset": 0.2,
                    "miss_num": 0,
                    "misalign_num": 0,
                    "total_num": 2,
                    "precision_rate": 1.0,
                },
            },
            {
                "name":
                "aligned some",
                "scofo_output": [
                    {
                        "est_time": 1.1,
                        "det_time": 1.2,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "est_time": 3.1,
                        "det_time": 3.2,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                    {
                        "est_time": 1000,
                        "det_time": 1000,
                        "note_start": 6,
                        "midi_note_num": 42,
                    },
                ],
                "ref": [
                    {
                        "tru_time": 1,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "tru_time": 3,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                    {
                        "tru_time": 5,
                        "note_start": 6,
                        "midi_note_num": 42,
                    },
                ],
                "want": {
                    "miss_rate": 0.0,
                    "misalign_rate": 0.3333333333333333,
                    "piece_completion": 0.6666666666666666,
                    "std_of_error": 0.0,
                    "mean_absolute_error": 0.1,
                    "std_of_latency": 0.0,
                    "mean_latency": 0.1,
                    "std_of_offset": 0.0,
                    "mean_absolute_offset": 0.2,
                    "miss_num": 0,
                    "misalign_num": 1,
                    "total_num": 3,
                    "precision_rate": 0.6666666666666666,
                },
            },
            {
                "name":
                "missed some",
                "scofo_output": [
                    {
                        "est_time": 1.1,
                        "det_time": 1.2,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "est_time": 3.1,
                        "det_time": 3.2,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                ],
                "ref": [
                    {
                        "tru_time": 1,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "tru_time": 3,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                    {
                        "tru_time": 5,
                        "note_start": 6,
                        "midi_note_num": 42,
                    },
                ],
                "want": {
                    "miss_rate": 0.3333333333333333,
                    "misalign_rate": 0.0,
                    "piece_completion": 0.6666666666666666,
                    "std_of_error": 0.0,
                    "mean_absolute_error": 0.1,
                    "std_of_latency": 0.0,
                    "mean_latency": 0.1,
                    "std_of_offset": 0.0,
                    "mean_absolute_offset": 0.2,
                    "miss_num": 1,
                    "misalign_num": 0,
                    "total_num": 3,
                    "precision_rate": 0.6666666666666666,
                },
            },
            {
                "name":
                "aligned ahead of time",
                "scofo_output": [
                    {
                        "est_time": 0.9,
                        "det_time": 0.95,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "est_time": 2.9,
                        "det_time": 2.95,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                ],
                "ref": [
                    {
                        "tru_time": 1,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "tru_time": 3,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                ],
                "want": {
                    "miss_rate": 0.0,
                    "misalign_rate": 0.0,
                    "piece_completion": 1.0,
                    "std_of_error": 0.0,
                    "mean_absolute_error": 0.1,
                    "std_of_latency": 0.0,
                    "mean_latency": 0.05,
                    "std_of_offset": 0.0,
                    "mean_absolute_offset": 0.05,
                    "miss_num": 0,
                    "misalign_num": 0,
                    "total_num": 2,
                    "precision_rate": 1.0,
                },
            },
            {
                "name":
                "test errors",
                "scofo_output": [
                    {
                        "est_time": 1.5,
                        "det_time": 1.7,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "est_time": 3.1,
                        "det_time": 3.2,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                ],
                "ref": [
                    {
                        "tru_time": 1,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "tru_time": 3,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                ],
                "want": {
                    "miss_rate": 0.0,
                    "misalign_rate": 0.0,
                    "piece_completion": 1.0,
                    "std_of_error": 0.2,
                    "mean_absolute_error": 0.3,
                    "std_of_latency": 0.05,
                    "mean_latency": 0.15,
                    "std_of_offset": 0.25,
                    "mean_absolute_offset": 0.45,
                    "miss_num": 0,
                    "misalign_num": 0,
                    "total_num": 2,
                    "precision_rate": 1.0,
                },
            },
            {
                "name":
                "aligned all (within 1ms bound in note_start)",
                "scofo_output": [
                    {
                        "est_time": 1.1,
                        "det_time": 1.2,
                        "note_start": 1,
                        "midi_note_num": 42,
                    },
                    {
                        "est_time": 3.1,
                        "det_time": 3.2,
                        "note_start": 5,
                        "midi_note_num": 42,
                    },
                ],
                "ref": [
                    {
                        "tru_time": 1,
                        "note_start": 2,
                        "midi_note_num": 42,
                    },
                    {
                        "tru_time": 3,
                        "note_start": 4,
                        "midi_note_num": 42,
                    },
                ],
                "want": {
                    "miss_rate": 0.0,
                    "misalign_rate": 0.0,
                    "piece_completion": 1.0,
                    "std_of_error": 0.0,
                    "mean_absolute_error": 0.1,
                    "std_of_latency": 0.0,
                    "mean_latency": 0.1,
                    "std_of_offset": 0.0,
                    "mean_absolute_offset": 0.2,
                    "miss_num": 0,
                    "misalign_num": 0,
                    "total_num": 2,
                    "precision_rate": 1.0,
                },
            },
        ]

        for tc in cases:
            got = match(tc["scofo_output"], tc["ref"])
            want = tc["want"]
            for key, val in got.items():
                self.assertAlmostEqual(want[key], val, 5,
                                       f'{tc["name"]}: {key}')  # type: ignore
コード例 #6
0
def bach10():
    import re
    import json
    from typing import Tuple, List
    from midi import process_midi
    from align import ASMAligner, alignment_repr
    from utils.sharedtypes import Alignment, NoteInfo, FollowerOutputLine
    from utils.eprint import eprint
    from utils.processfile import process_ref_file
    from utils.match import match

    BACH10_PATH = os.path.join(DATA_PATH, "Bach10_v1.1")
    OUTPUT_PATH = os.path.join(REPRO_RESULTS_PATH, "bach10")
    os.makedirs(OUTPUT_PATH, exist_ok=True)
    BACH10_PIECE_PATHS = [
        f.path for f in os.scandir(BACH10_PATH) if f.is_dir()
        and bool(re.search(r"^[0-9]{2}-\w+$", os.path.basename(f.path)))
    ]
    BACH10_PIECE_BASENAMES = [os.path.basename(x) for x in BACH10_PIECE_PATHS]

    class Bach10Piece:
        def __init__(self, name: str, postalignthres: float):
            self.name = name
            self.dirpath = os.path.join(BACH10_PATH, name)
            self.refalignpath = os.path.join(self.dirpath, f"{self.name}.txt")
            self.rscorepath = os.path.join(self.dirpath, f"{self.name}.mid")
            self.pscore: List[NoteInfo] = self._refalign_to_pscore()
            self.rscore: List[NoteInfo] = process_midi(self.rscorepath)
            self.postalignthres = postalignthres

        def align(self) -> Alignment:
            """
            align and return (alignment)
            """
            eprint(f"Aligning {self.name}")
            aligner = ASMAligner(self.pscore, self.rscore, self.postalignthres)
            alignment = aligner.get_alignment()

            return alignment

        def _refalign_to_pscore(self) -> List[NoteInfo]:
            f = open(self.refalignpath)
            t = f.read().strip()
            f.close()

            def process_line(line: str) -> NoteInfo:
                ls = line.split()
                if len(ls) < 4:
                    raise ValueError(f"Too few entries on line: {line}")
                # (performance time (ms), MIDI note num)
                return {
                    "note_start": float(ls[0]),
                    "midi_note_num": int(ls[2])
                }

            return list(map(process_line, t.splitlines()))

    def align_piece(name: str, postalignthres: float) -> Alignment:
        p = Bach10Piece(name, postalignthres)
        return p.align()

    def alignment_to_follower_output(
            alignment: Alignment) -> List[FollowerOutputLine]:
        return [{
            "est_time": n["p"]["note_start"],
            "det_time": n["p"]["note_start"],
            "note_start": n["s"]["note_start"],
            "midi_note_num": n["p"]["midi_note_num"],
        } for n in alignment if n["p"] is not None and n["s"] is not None
                and n["p"]["midi_note_num"] == n["s"]["midi_note_num"]]

    postalignthres: float = -1  # not needed
    alignments: List[Tuple[str,
                           Alignment]] = [(name,
                                           align_piece(name, postalignthres))
                                          for name in BACH10_PIECE_BASENAMES]

    for name, alignment in alignments:
        out_path = os.path.join(OUTPUT_PATH, name)
        os.makedirs(out_path, exist_ok=True)
        stat_file_path = os.path.join(out_path, f"{name}.align.stat")
        align_file_path = os.path.join(out_path, f"{name}.align.txt")
        sf = open(stat_file_path, "w")
        af = open(align_file_path, "w")
        stdout, stderr = alignment_repr(alignment)
        sf.write(stderr)
        af.write(stdout)
        sf.close()
        af.close()

        follower_output = alignment_to_follower_output(alignment)
        ref_contents = process_ref_file(
            os.path.join(BACH10_PATH, name, f"{name}.txt"))

        res = match(follower_output, ref_contents)
        res_str = json.dumps(res, indent=4)

        res_file_path = os.path.join(out_path, f"{name}.scofo.json")
        rf = open(res_file_path, "w")
        rf.write(res_str)
        rf.close()

    print(f"OUTPUT: {OUTPUT_PATH}")
コード例 #7
0
    def forward(self, predictions, targets):
        """
        損失関数の計算。

        Parameters
        ----------
        predictions : SSD netの訓練時の出力(tuple)
            (loc=torch.Size([num_batch, 8732, 4]), conf=torch.Size([num_batch, 8732, 21]), dbox_list=torch.Size [8732,4])。

        targets : [num_batch, num_objs, 5]
            5は正解のアノテーション情報[xmin, ymin, xmax, ymax, label_ind]を示す

        Returns
        -------
        loss_l : テンソル
            locの損失の値
        loss_c : テンソル
            confの損失の値

        """

        # SSDモデルの出力がタプルになっているので、個々にばらす
        loc_data, conf_data, dbox_list = predictions

        # 要素数を把握
        num_batch = loc_data.size(0)  # ミニバッチのサイズ
        num_dbox = loc_data.size(1)  # DBoxの数 = 8732
        num_classes = conf_data.size(2)  # クラス数 = 21

        # 損失の計算に使用するものを格納する変数を作成
        # conf_t_label:各DBoxに一番近い正解のBBoxのラベルを格納させる
        # loc_t:各DBoxに一番近い正解のBBoxの位置情報を格納させる
        conf_t_label = torch.LongTensor(num_batch, num_dbox).to(self.device)
        loc_t = torch.Tensor(num_batch, num_dbox, 4).to(self.device)

        # loc_tとconf_t_labelに、
        # DBoxと正解アノテーションtargetsをmatchさせた結果を上書きする
        for idx in range(num_batch):  # ミニバッチでループ

            # 現在のミニバッチの正解アノテーションのBBoxとラベルを取得
            truths = targets[idx][:, :-1].to(self.device)  # BBox
            # ラベル [物体1のラベル, 物体2のラベル, …]
            labels = targets[idx][:, -1].to(self.device)

            # デフォルトボックスを新たな変数で用意
            dbox = dbox_list.to(self.device)

            # 関数matchを実行し、loc_tとconf_t_labelの内容を更新する
            # (詳細)
            # loc_t:各DBoxに一番近い正解のBBoxの位置情報が上書きされる
            # conf_t_label:各DBoxに一番近いBBoxのラベルが上書きされる
            # ただし、一番近いBBoxとのjaccard overlapが0.5より小さい場合は
            # 正解BBoxのラベルconf_t_labelは背景クラスの0とする
            variance = [0.1, 0.2]
            # このvarianceはDBoxからBBoxに補正計算する際に使用する式の係数です
            match(self.jaccard_thresh, truths, dbox,
                  variance, labels, loc_t, conf_t_label, idx)

        # ----------
        # 位置の損失:loss_lを計算
        # Smooth L1関数で損失を計算する。ただし、物体を発見したDBoxのオフセットのみを計算する
        # ----------
        # 物体を検出したBBoxを取り出すマスクを作成
        pos_mask = conf_t_label > 0  # torch.Size([num_batch, 8732])

        # pos_maskをloc_dataのサイズに変形
        pos_idx = pos_mask.unsqueeze(pos_mask.dim()).expand_as(loc_data)

        # Positive DBoxのloc_dataと、教師データloc_tを取得
        loc_p = loc_data[pos_idx].view(-1, 4)
        loc_t = loc_t[pos_idx].view(-1, 4)

        # 物体を発見したPositive DBoxのオフセット情報loc_tの損失(誤差)を計算
        loss_l = F.smooth_l1_loss(loc_p, loc_t, reduction='sum')

        # ----------
        # クラス予測の損失:loss_cを計算
        # 交差エントロピー誤差関数で損失を計算する。ただし、背景クラスが正解であるDBoxが圧倒的に多いので、
        # Hard Negative Miningを実施し、物体発見DBoxと背景クラスDBoxの比が1:3になるようにする。
        # そこで背景クラスDBoxと予想したもののうち、損失が小さいものは、クラス予測の損失から除く
        # ----------
        batch_conf = conf_data.view(-1, num_classes)

        # クラス予測の損失を関数を計算(reduction='none'にして、和をとらず、次元をつぶさない)
        if not self.floss:
            loss_c = F.cross_entropy(
                batch_conf, conf_t_label.view(-1), reduction='none')
            
            num_pos = pos_mask.long().sum(1, keepdim=True)  # ミニバッチごとの物体クラス予測の数
            loss_c = loss_c.view(num_batch, -1)  # torch.Size([num_batch, 8732])
            loss_c[pos_mask] = 0  # 物体を発見したDBoxは損失0とする

            # Hard Negative Miningを実施する
            # 各DBoxの損失の大きさloss_cの順位であるidx_rankを求める
            _, loss_idx = loss_c.sort(1, descending=True)
            _, idx_rank = loss_idx.sort(1)

            num_neg = torch.clamp(num_pos*self.negpos_ratio, max=num_dbox)

            # idx_rankは各DBoxの損失の大きさが上から何番目なのかが入っている
            # 背景のDBoxの数num_negよりも、順位が低い(すなわち損失が大きい)DBoxを取るマスク作成
            # torch.Size([num_batch, 8732])
            neg_mask = idx_rank < (num_neg).expand_as(idx_rank)

            pos_idx_mask = pos_mask.unsqueeze(2).expand_as(conf_data)
            neg_idx_mask = neg_mask.unsqueeze(2).expand_as(conf_data)

            # conf_dataからposとnegだけを取り出してconf_hnmにする。形はtorch.Size([num_pos+num_neg, 21])
            conf_hnm = conf_data[(pos_idx_mask+neg_idx_mask).gt(0)
                                 ].view(-1, num_classes)

            conf_t_label_hnm = conf_t_label[(pos_mask+neg_mask).gt(0)]

            # confidenceの損失関数を計算(要素の合計=sumを求める)
            if not self.floss:
                loss_c = F.cross_entropy(conf_hnm, conf_t_label_hnm, reduction='sum')
            else:
                loss_c = self.focal(conf_hnm, conf_t_label_hnm)

            # 物体を発見したBBoxの数N(全ミニバッチの合計)で損失を割り算
            N = num_pos.sum()
            loss_l /= N
            loss_c /= N
        else:
            loss_c = self.focal(batch_conf, conf_t_label.view(-1))


        return loss_l, loss_c