<jupyter_start><jupyter_text>读取数据分析分布<jupyter_code>df = pd.read_excel('./work/dataset/Train/Fovea_Location_train.xlsx')
df['path'] = './work/dataset/Train/fundus_image/' + df['imgName']
df['size'] = df['path'].apply(lambda path: Image.open(path).size)
df['mode'] = df['path'].apply(lambda path: Image.open(path).mode)
df['nosee'] = df.apply(lambda row: 1 if (row['Fovea_X']+row['Fovea_Y'])<10 else 0, axis=1)
df.head(2)
print('图像大小\n', df['size'].value_counts())
print('图像模式\n', df['mode'].value_counts())
df.sort_values('Fovea_X').head(50) # 不可见的样本
df_test = pd.DataFrame(os.listdir('./work/dataset/PALM-Testing400-Images'), columns=['FileName'])
df_test = df_test.sort_values('FileName').reset_index(drop=True)
df_test['path'] = './work/dataset/PALM-Testing400-Images/' + df_test['FileName']
df_test['size'] = df_test['path'].apply(lambda path: Image.open(path).size)
df_test['mode'] = df_test['path'].apply(lambda path: Image.open(path).mode)
df_test.head(2)<jupyter_output><empty_output><jupyter_text>基线程序<jupyter_code>def get_angle(x, y):
    # 0 -> 360°
    ang = math.atan2(y, x)
    ang = ang * 180 / math.pi
    if ang > 360:
        ang -= 360
    if ang < 0:
        ang += 360
    return ang
get_angle(0,0.1)

class PALMDataset(Dataset):
    def __init__(self, df, mode='train'):
        self.mode = mode
        self.df = df.reset_index(drop=True)
        self.nlen = len(df)
        # self.common_before_trans = T.Compose([
        #     T.Resize(size=(404,404))
        # ])
        self.common_after_trans = T.Compose([
            T.Resize(size=(404,404)),
            T.ToTensor(),
            T.Normalize(mean=(0.496,0.456,0.406), std=(0.229,0.224,0.225))
        ])
        self.enhance_op = [
            self.rotate_trans,
            self.colorjitter_trans,
            self.horizontalFlip_trans,
            self.verticalFlip_trans,
        ]
    
    def rotate_trans(self, img, xy):
        pos_x, pos_y = xy
        pos_x, pos_y = pos_x-0.5, pos_y-0.5 # 以中心为坐标原点
        if abs(pos_x)<1e-4 and abs(pos_y)<1e-4:
            return img, xy # 靠近旋转中心, 意义不大
        ang_0 = get_angle(pos_x, pos_y)
        lens = np.linalg.norm([pos_x, pos_y])
        try_times = 0
        while True:
            rot_ang = random.random()*60-30 # 随机旋转±30°
            new_ang = (ang_0 + rot_ang)/180.0 * math.pi
            new_pos_x = lens*math.cos(new_ang)
            new_pos_y = lens*math.sin(new_ang)
            if abs(new_pos_x)<0.45 or abs(new_pos_y)<0.45: # 取0.45, 避免落在边界
                pos_x = new_pos_x + 0.5
                pos_y = new_pos_y + 0.5
                break
            try_times += 1
            if try_times > 10: # 避免死循环
                pos_x += 0.5
                pos_y += 0.5
                rot_ang = 0
                break
        img = T.rotate(img, rot_ang)
        return img, (pos_x, pos_y)
    
    def colorjitter_trans(self, img, xy):
        img = T.ColorJitter(0.2,0.2,0.2,0.02)(img)
        return img, xy

    def horizontalFlip_trans(self, img, xy):
        img = T.hflip(img)
        pos_x, pos_y = xy
        return img, (1-pos_x, pos_y)
    
    def verticalFlip_trans(self, img, xy):
        img = T.vflip(img)
        pos_x, pos_y = xy
        return img, (pos_x, 1-pos_y)

    
    def enhance(self, img, pos: tuple):
        # img = self.common_before_trans(img)
        if self.mode == 'train':
            for trans_fun in self.enhance_op:
                # print(trans_fun, pos_fun)
                if random.random()>0.5:
                    img, pos = trans_fun(img, pos)
        img = self.common_after_trans(img)
        return img, pos


    def __len__(self):
        return self.nlen

    def __getitem__(self, idx):
        path = self.df.loc[idx, 'path']
        img = Image.open(path)
        pos = (0, 0)
        if self.mode == 'train':
            width, height = self.df.loc[idx, 'size']
            raw_posx, raw_posy = self.df.loc[idx, 'Fovea_X'], self.df.loc[idx, 'Fovea_Y']
            pos = (raw_posx/width, raw_posy/height)
        img, pos = self.enhance(img, pos)
        return img.astype('float32'), np.array(pos, dtype='float32')

df_train, df_dev = train_test_split(df, test_size=0.2, stratify=df['nosee'], random_state=76)
print(df_train.shape, df_dev.shape)

train_ds = PALMDataset(df_train, 'train')
dev_ds = PALMDataset(df_dev, 'dev')
print(len(train_ds), len(dev_ds))
print(train_ds[10][0].shape, train_ds[10][1])

class PAMLNet(nn.Layer):
    def __init__(self):
        super(PAMLNet, self).__init__()
        self.backbone = paddle.vision.mobilenet_v2(pretrained=False, num_classes=-1)
        self.fc = nn.Sequential(
            nn.Flatten(1),
            nn.Dropout(0.1),
            nn.Linear(1280, 64),
            nn.ELU(),
            nn.Linear(64, 2),
        )
    def forward(self, x):
        x = self.backbone(x)
        x = self.fc(x)
        return x

model = paddle.Model(PAMLNet())
# model.summary((1, 3,404,608))
# paddle.summary(paddle.vision.resnet101(pretrained=False, num_classes=-1), (1, 3, 204,204))
class PALMMetric(paddle.metric.Metric):
    def __init__(self, name='palm', *args, **kwargs):
        super(PALMMetric, self).__init__(*args, **kwargs)
        self._name = name
        self.reset()


    def update(self, pred, label):
        if self.pred_pos is None:
            self.pred_pos = pred
            self.real_pos = label
        else:
            self.pred_pos = np.r_[self.pred_pos, pred]
            self.real_pos = np.r_[self.real_pos, label]

    def reset(self):
        self.pred_pos = None
        self.real_pos = None

    def accumulate(self):
        err_dis = np.linalg.norm(self.pred_pos-self.real_pos, axis=1).mean()
        return 1.0/err_dis

    def name(self):
        return self._name
schedual = paddle.optimizer.lr.CosineAnnealingDecay(5e-4, T_max=80)
model.prepare(
    optimizer=paddle.optimizer.Adam(parameters=model.parameters(), learning_rate=schedual),
    loss=nn.SmoothL1Loss(),
    metrics=PALMMetric()
)
train_loader = DataLoader(train_ds, batch_size=16, shuffle=True, num_workers=2)
dev_loader = DataLoader(dev_ds, batch_size=16, shuffle=False, num_workers=2)

model.fit(
    train_loader,
    dev_loader,
    epochs=50,
    batch_size=16,
    save_dir='./work/model',
    save_freq=10,
    callbacks=[paddle.callbacks.EarlyStopping(monitor='palm',save_best_model=True, patience=20)]
)
model.save('./work/11111036')
test_ds = PALMDataset(df_test, 'test')
print(len(test_ds))
print(test_ds[0][1])
y_pred = model.predict(test_ds, batch_size=32, stack_outputs=True)[0]
df_test[['Fovea_X','Fovea_Y']] =  y_pred
df_test['Fovea_X'] *= df_test['size'].apply(lambda x: x[0])
df_test['Fovea_Y'] *= df_test['size'].apply(lambda x: x[1])
df_test.head(2)
df_test[['FileName','Fovea_X','Fovea_Y']].to_csv('Fovea_Localization_Results_3.csv', index=False)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.15193008011653314, 'fraction_numerical': 0.0314639475600874, 'mean_word_length': 2.625131995776135, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '88608', 'license': 'Apache-2.0', 'n_tokens_mistral': 2763, 'n_tokens_neox': 2630, 'n_words': 425, 'path': 'main.ipynb', 'repo_name': 'quhezuo/aistudio', 'size': '6865'}