主頁 >  其他 > 建立一個植物毒性分類器:資料準備和清理

建立一個植物毒性分類器:資料準備和清理

2020-11-12 06:45:30 其他

作者|Kenichi Nakanishi
編譯|VK
來源|Towards Data Science

我有一個愛買植物的未婚妻,還有一只愛啃植物的貓——我想,有什么比把一個能告訴我植物是否安全的分類器更好呢!

需要注意的一點是,這里所做的所有作業都是在google colabs上完成的,使用的notebook可以在我的Github上找到:https://github.com/kenichinakanishi/houseplant_classifier


步驟1-獲取資料

不幸的是,我找不到一個適合我在Kaggle上或使用Google的資料集搜索的預先制作的影像資料集,所以,我準備建立我自己的!

我決定使用ASPCA的《貓和狗的植物毒性清單》,我已經用了好幾次了,這給了我們一個很好的核心作業,為了從網站上獲取這些文本資料,我們可以求助于BeautifulSoup,這是一個Python庫,用于從HTML和XML檔案中提取資料,

from urllib.request import Request, urlopen
from bs4 import BeautifulSoup

def getHTMLContent(link):
    html = urlopen(link)
    soup = BeautifulSoup(html, 'html.parser')
    return soup

然而,當查看他們的網站時,該表并不是一個易于訪問的html表,而是將資料存盤為面板中的行,幸運的是,beauthulsoup為我們提供了一種簡單的方法來搜索決議樹,以找到我們想要的資料,例如:

req = Request('https://www.aspca.org/pet-care/animal-poison-control/cats-plant-list', headers={'User-Agent': 'Mozilla/5.0'})
webpage = urlopen(req).read()

# 爬取資料
soup = BeautifulSoup(webpage, 'lxml')    

# 搜索決議樹以從表中獲得所有內容  
content_list = soup.find_all('span')[7:-4]       

# 將其放入一個dataframe中進行進一步處理
df_cats = pd.DataFrame(content_list)  

在收集完原始資料后,我們需要將其分為多個列,并進行一些拆分:

# 清理字串
df_cats[0] = df_cats[0].apply(lambda x: str(x).split('>')[1][:-3])
df_cats[4] = df_cats[4].apply(lambda x: str(x).split('>')[1][:-3])
df_cats[1] = df_cats[1].apply(lambda x: str(x).split('(')[1][0:-4])

# 洗掉無用的列并重命名列
df_cats = df_cats.drop(columns=[2,3,5,6]).rename(columns = {0:'Name',1:'Alternative Names',4:'Scientific Name',7:'Family'})

# 將有毒和無毒植物分開
df_cats['Toxic to Cats'] = True
first_nontoxic_cats = [index for index in df_cats[df_cats['Name'].str.startswith('A')].index if index>100][0]
df_cats.loc[first_nontoxic_cats:,'Toxic to Cats'] = False

然后,我們可以對特定于狗的串列重復此程序,然后合并資料幀并清理nan:

# 合并資料框架到一個,用于保留只存在于一邊的值
df_catsdogs = df_dogs.merge(df_cats, how='outer', on=['Name','Alternative Names','Scientific Name','Family'])
df_catsdogs = df_catsdogs.fillna('Unknown')
aspca_df = df_catsdogs.copy()

# 假設對貓和狗有相同的毒性
aspca_df['Toxic to Cats'] = aspca_df.apply(lambda x: x['Toxic to Dogs'] if (x['Toxic to Cats'] == 'Unknown') else x['Toxic to Cats'], axis=1)
aspca_df['Toxic to Dogs'] = aspca_df.apply(lambda x: x['Toxic to Cats'] if (x['Toxic to Dogs'] == 'Unknown') else x['Toxic to Dogs'], axis=1)

步驟2-淺度清理

接下來,我們可以開始進行淺度清理,包括查看資料集,決定要使用哪些關鍵特征,并標準化它們的格式,

我們目前有名字,替代名稱,學名,家族以及毒性列,所有這些都是從用BeautifulSoup在ASPCA網站上爬來的,

由于我們將使用谷歌影像搜索收集影像,因此我們決定根據每種植物的確切學名進行搜索,以獲得盡可能具體的影像,像“珍珠點”、“大象耳朵”、“蓬松褶邊”和“粉紅珍珠”這樣的名字會很快回傳我們所尋找的植物之外的結果,

我們撰寫了幾個快速函式來應用于該系列,以嘗試將資料標準化以便進一步清理,

# 確保每個學名的標點符號正確
def normalize_capitalization(x):
  first_word, rest = x.split()[0], x.split()[1:]
  first_word = [first_word.capitalize()]
  rest = [word.lower() for word in rest]
  return ' '.join(first_word+rest)

# 清理那些名字不同的重復物種
def species_normalizer(word):
  if word.split()[-1] in ['sp','species','spp','sp.','spp.']:
    word = ''.join(word.split()[:-1])
  return word

# 從名稱中洗掉cv,因為這是一種過時的表示品種的方式
def cv_remover(word):
  if 'cv' in word:
    word = word.replace(' cv ',' ')
  return word

# 從名稱中洗掉var
def var_remover(word):
  if 'var' in word:
    word = word.replace(' var. ',' ')
  return word

# 應用每個函式
aspca_df['Scientific Name'] = aspca_df['Scientific Name'].apply(normalize_capitalization)
aspca_df['Scientific Name'] = aspca_df['Scientific Name'].apply(species_normalizer)
aspca_df['Scientific Name'] = aspca_df['Scientific Name'].apply(cv_remover)
aspca_df['Scientific Name'] = aspca_df['Scientific Name'].apply(var_remover)

# 洗掉特殊字符
aspca_df['Scientific Name'] = aspca_df['Scientific Name'].apply(lambda x: ''.join([character for character in x if character.isalnum() or character.isspace()]))

# 進一步處理重置資料
aspca_df = aspca_df.sort_values('Scientific Name').drop_duplicates('Scientific Name')
aspca_df = aspca_df.reset_index(drop=True).sort_index()

步驟3-通過交叉參考進行深度清理

仔細研究一下我們的資料里的學名(Scientific Name),我們發現很多名稱是物種的過時同義詞,或者拼寫錯誤,這將在影像采集和以后的訓練模型識別具有不同標簽的相同影像時引起問題,

一個谷歌之后,我們發現了世界植物在線資料庫,一個開放存取的,基于網路的世界植物物種簡編(http://www.worldfloraonline.org/),它們列出了同義詞和公認的物種名稱,并由“分類學專家網路”定期更新,非常適合交叉參考我們不可靠的學名,這個資料庫以一個.txt檔案提供了它們的資料,我們可以讀入該檔案并與從ASPCA植物毒性資料庫中獲取的資料庫進行比較,

# 讀取WFO資料,只保留有用的列
use_cols = ['scientificName','taxonRank','family','genus','taxonomicStatus','taxonID', 'acceptedNameUsageID']
wfo_df = pd.read_csv('/content/drive/My Drive/Houseplant Classifier/classification.txt', sep='\t', lineterminator='\n', usecols=use_cols)
wfo_df = wfo_df.sort_values('taxonomicStatus')

作為第一步,我們將對來自ASPCA的資料進行左合并,保留我們的所有類,并添加與我們當前擁有的確切學名匹配的任何資料,我們的目標是將資料庫中的所有植物更新為最新的可接受的學名,

# 不需要這個列,我們更信任WFO資料庫
aspca_df.drop('Family', axis=1, inplace=True)

# 合并資料檔案以獲得可信資訊
aspca_df = aspca_df.merge(wfo_df, how = 'left', left_on = ['Scientific Name'], right_on = ['scientificName'])

# 按taxonomicStatus進行排序,并洗掉重復項,保持優先級為被接受的名稱
aspca_df = aspca_df.sort_values('taxonomicStatus').drop_duplicates('Scientific Name', keep='first').reset_index(drop=True)

# 用Unknown來填滿NaN
aspca_df = aspca_df.fillna('Unknown')

步驟3.1-用字串匹配修復印刷錯誤

許多學名指的是同一物種,但由于在ASPCA資料庫中的打字錯誤,有幾個字母被刪掉了,讓我們使用difflib中的SequenceMatcher來量化字串距離,通過比較WFO資料庫中不匹配的條目來發現這些錯誤,

我們可以對資料幀進行排序,只與以同一字母開頭的學名進行比較,以節省時間,如果名稱足夠相似,我們將保留它并最侄訓傳最接近的匹配項,這里我們將閾值設定為0.9,以避免任何不正確的匹配,

def get_closest_name(unknown_name, name_df = wfo_df, name_col = 'scientificName', threshold=0.9, verbose=False):
  """ 將'unknown_name'與'name_df'中接受的名稱進行匹配,將回傳超過接近的“threshold”的名字. 

  Parameters
  ----------
  unknown_name: str
    我們希望與該名稱進行匹配. 
  name_df: DataFrame
    包含名稱的資料框.
  name_col: str, name of name_df column 
    包含可接受名稱的列
  threshold: int
    unknown_name需要在多大程度上與接受的名稱匹配
    如果超過這個閾值,名稱將被添加到可能的名稱字典中
  verbose: bool
    函式是否列印整個串列

  Returns:
  ----------
  str
    與‘unknown_name’最接近的、高于給定‘閾值’的名稱,
  """
  import operator
  from difflib import SequenceMatcher
  def similar(a, b):
      return SequenceMatcher(None, a, b).ratio()
  poss_names = {}
    
  # 為了節省時間,只看第一個字母相同的條目
  for true_sciname in name_df[name_df[name_col].str.startswith(unknown_name[0])][name_col].values:
    similar_score = similar(unknown_name, true_sciname)
    if similar_score>threshold:
      poss_names[true_sciname]=similar_score
    
  # 如果dict為空
  if verbose == True:
    print(poss_names)
  if not bool(poss_names):
    print(f'No names close enough to {unknown_name}.')
    return ''
  else:
    print(f'{unknown_name} is closest to {max(poss_names.items(), key=operator.itemgetter(1))[0]}, with a score of {max(poss_names.items(), key=operator.itemgetter(1))[1]:.2f}')
    return max(poss_names.items(), key=operator.itemgetter(1))[0]

我們還定義了一個函式來修復資料中的問題條目,它將把它們的學名、科、屬和分類狀態更新為WFO資料庫中的(正確的)相應條目,

def fix_name(unknown_name, true_name):
  """ 根據已接受的wfo_df條目修復aspca_df條目.

  Parameters
  ----------
  unknown_name: str
    我們想要修復的名字. 
  true_name: DataFrame
    修復的名稱.
  """

  #得到我們想要改變的列
  unknown_data = https://www.cnblogs.com/panchuangai/p/aspca_df[aspca_df['Scientific Name'] == unknown_name]

  # 根據ID查找從wfo資料庫中獲取已接受的資料
  true_data = https://www.cnblogs.com/panchuangai/p/wfo_df[wfo_df['scientificName'] == true_name]
  true_sciname = true_data.loc[:,'scientificName'].values[0]
  true_family = true_data.loc[:,'family'].values[0]
  true_genus = true_data.loc[:,'genus'].values[0]
  true_taxonomicStatus = true_data.loc[:,'taxonomicStatus'].values[0]

  # 更改學名、科、屬和分類學地位為可接受的版本
  aspca_df.iloc[unknown_data.index,2] = true_sciname
  aspca_df.iloc[unknown_data.index,8] = true_family
  aspca_df.iloc[unknown_data.index,9] = true_genus
  aspca_df.iloc[unknown_data.index,10] = true_taxonomicStatus

現在,我們可以遍歷我們的資料,搜索匹配的名稱并當場更正它們對應的資料幀條目,

unknown_idx = aspca_df[aspca_df.taxonomicStatus == 'Unknown'].index
print(f'{len(unknown_idx)} plants currently cannot be matched.')
from tqdm.notebook import tqdm
for i in tqdm(unknown_idx):
  unknown_name = aspca_df.iloc[i,2]
  closest_name = get_closest_name(unknown_name)
  if closest_name == '':
    continue
  fix_name(unknown_name,closest_name)

此程序有助于我們發現錯誤,否則需要進行深入的檢查

步驟3.2-人工清理不明物種

不幸的是,許多未被確認的物種在資料庫中沒有一個足夠接近的條目,因此,我們對剩余的未知項進行一些手動修復,謝天謝地,上面的代碼將需要手動關注的樣本數量減少到了50個左右,我們可以重新使用之前的fix_name函式,根據我們在Google上找到的正確條目來修復這些條目,

步驟3.3-匹配同義學名

既然學名已經全部更正,我們仍然需要對它們進行標準化,因為隨著研究的更新,學名可能會隨著時間的推移而改變(導致在“分類狀態”列中出現同義詞標簽),如果一個學名是一個公認的名字的同義詞,我們希望在將來的谷歌影像搜索中使用這個被接受的名字,

# 更新剩下的已接受的學名的同義詞學名
aspca_df = aspca_df.sort_values('taxonomicStatus').drop_duplicates('Scientific Name', keep='first').reset_index(drop=True)
synonym_idx = aspca_df[aspca_df['taxonomicStatus'].values == 'Synonym'].index
for i in synonym_idx:
    
  # 得到我們想要改變的列
  synonym_data = https://www.cnblogs.com/panchuangai/p/aspca_df.iloc[i,:]
  synonym_name = synonym_data.loc['Scientific Name']

  # 根據ID查找從wfo資料庫中獲取已接受的資料
  true_data = https://www.cnblogs.com/panchuangai/p/wfo_df[wfo_df['taxonID'] == synonym_data.loc['acceptedNameUsageID']]
  true_sciname = true_data.iloc[:,1].values[0]
  fix_name(synonym_name,true_sciname)

幸運的是,WFO資料庫包含一個acceptedNameUsageID欄位,該欄位包含給定同義學名的可接受名稱,我們可以利用該欄位查找接受的學名并將其傳遞到fix_name函式中,

步驟3.4-結束

現在,我們已經糾正了拼寫錯誤(自動和手動),并將發回的同義詞與最新的已接受名稱進行了匹配,剩下的就是清理影像下載的資料幀,

# 再次排序并洗掉
aspca_df = aspca_df.sort_values('taxonomicStatus').drop_duplicates('Scientific Name', keep='first')
aspca_df = aspca_df.sort_values('Scientific Name').reset_index(drop=True).sort_index()

# 設定一個單詞名稱的屬作為名稱,而不是NaN
aspca_df.loc[aspca_df.fillna('Unknown')['genus']=='Unknown', 'genus'] = aspca_df.loc[aspca_df.fillna('Unknown')['genus']=='Unknown', 'Scientific Name']

# 洗掉我們不再需要的行
aspca_df = aspca_df.drop(['taxonID', 'scientificName', 'taxonomicStatus', 'acceptedNameUsageID', 'taxonRank'], axis=1)

# 標準化列名
aspca_df.rename(columns = {'genus':'Genus', 'family':'Family'}, inplace=True)

# 重新排序
cols = ['Name', 'Scientific Name', 'Genus', 'Family', 'Alternative Names', 'Toxic to Dogs', 'Toxic to Cats']
aspca_df = aspca_df[cols]

這個程序需要多次迭代才能使方法正確,然而,在我們建立影像資料庫之前,確保我們有干凈的資料可以作業,這在花費時間訓練模型之前是至關重要的,

從最終的寵物植物毒性資料框架中得出一些有趣的結論:

  • 110個植物家族中有33個并非完全有毒或無毒,

  • 350個植物屬中有7個并非完全有毒或無毒,

  • 只有兩種植物表現出物種特異性毒性,莉莉花對貓和核桃對狗!

步驟4-下載影像

下載影像的第一步是獲取我們想要獲取的每個影像的url,為此,我們根據fabianbosler的一篇文章,采用了一種基于Selenium的方法,

Selenium是一個用于測驗web應用程式的可移植框架,Selenium webdriver充當我們的虛擬瀏覽器,可以通過python命令進行控制,

這里使用一個腳本來搜索Google圖片,我們給它一個查詢,只查找和下載縮略圖的網址,因為我們要抓取很多圖片,一個問題是,谷歌的許多影像縮略圖存盤為base64編碼的影像,我們還想抓取這些圖片,這樣我們就不會錯過任何具有高度相關性的圖片,因為我們在搜索結果中走的越遠,這些圖片就越不適合用于訓練目的,

# 如果運行在Colab
!pip install selenium -q
!apt-get update # to update ubuntu to correctly run apt install
!apt install chromium-chromedriver -q
!cp /usr/lib/chromium-browser/chromedriver /usr/bin
import sys
sys.path.insert(0,'/usr/lib/chromium-browser/chromedriver')

# 匯入并設定Selenium webdriver
from selenium import webdriver
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
wd = webdriver.Chrome('chromedriver',chrome_options=chrome_options)
import requests
import time

def fetch_thumbnail_urls(query:str, max_links_to_fetch:int, wd:webdriver, sleep_between_interactions:int=1, non_commercial=False, shuffle=False):
    """ 使用Selenium webdriver (wd)根據查詢從谷歌影像中收集url
    可以將sleep_between_interactions更改為適應較慢的計算機,
    如果shuffle為真,則回傳的url串列將被打亂為隨機順序

    Parameters
    ----------
    query: str
      傳遞給谷歌影像,
    max_links_to_fetch: int
      要獲取的url數目,
    wd: Selenium webdriver
      要使用的webdriver實體,
    sleep_between_interactions: int
       在webdriver互動之間等待的時間(秒),
    non_commercial: bool
      標記僅為非商業用途, 
    shuffle: bool
      回傳的url順序是否打亂,

    Returns:
    ----------
    List
      url的串列,
    """
    def scroll_to_end(wd):
        wd.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        time.sleep(sleep_between_interactions)    
    
    # 構建谷歌查詢
    if non_commercial == True:
      search_url = 'https://www.google.com/search?as_st=y&source=hp&safe=off&tbm=isch&as_epq={q}&gs_l=img&tbs=sur%3Af'
    else:
      search_url = "https://www.google.com/search?as_st=y&source=hp&safe=off&tbm=isch&as_epq={q}&gs_l=img"
    
    # 加載頁面
    wd.get(search_url.format(q=query))

    image_urls = []
    image_count = 0
    results_start = 0
    while image_count < max_links_to_fetch:
        scroll_to_end(wd)

        # 獲得所有影像縮略圖結果
        thumbnail_results = wd.find_elements_by_css_selector("img.Q4LuWd")
        number_results = len(thumbnail_results)
        
        for img in thumbnail_results:
            # 提取影像url,如果它們是可用的地址
            if img.get_attribute('src') and 'http' in img.get_attribute('src'):
                image_urls.append(img.get_attribute('src'))
                
            # 還獲取了谷歌使用的編碼影像
            elif img.get_attribute('src') and 'data' in img.get_attribute('src'):
                image_urls.append(img.get_attribute('src'))

            image_count = len(image_urls)

            # 如果我們達到指定的配額就中斷
            if len(image_urls) >= max_links_to_fetch:
                break
        # 如果我們需要更多的圖片,點擊加載更多圖片按鈕     
        else:
            time.sleep(30)
            load_more_button = wd.find_element_by_css_selector(".mye4qd")
            if load_more_button:
                wd.execute_script("document.querySelector('.mye4qd').click();")

        # 移動指標
        results_start = len(thumbnail_results)

    if shuffle==True:
      random.shuffle(image_urls)

    return image_urls

太好了!現在我們有了一種從谷歌圖片中獲取圖片的方法!為了下載我們的圖片,我們將利用fast.ai v2,然而,我們將深入研究源代碼并對其進行一點升級,以便在影像進入時對其進行哈希處理,并忽略/洗掉任何重復項,以便最終得到一致的唯一影像集,我們還將允許它解碼和下載編碼的.jpg和.png影像,這是谷歌影像用來存盤縮略圖的格式,

# 每個會話運行一次
!pip install fastai==2.0.14 -q
from fastai.vision.all import *
import io
from PIL import Image
import base64
import hashlib
def download_images(dest, url_file=None, urls=None, max_pics=150, n_workers=1, timeout=4):
    """
      下載文本檔案' url_file '中列出的圖片到路徑' dest ',最多下載' max_pics '個
      下載影像后,在保存之前將哈希與其他影像哈希進行比較,
	  如果哈希已經存在,則嘗試下一個url,

    Parameters
    ----------
    dest: Path or str
      下載目標檔案夾,
    url_file: 
      URL檔案,\n作為分隔符
    urls:
     url的串列,
    max_pics: int
       要下載的影像數量,
    n_workers: int
      要并行使用的內核數量,

    Returns:
    ----------
    從給定的url下載影像到dest目錄,
    """
    hash_keys = dict()
    
    # 設定哈希以防止復制影像下載
    if urls is None: urls = url_file.read().strip().split("\n")
    dest = Path(dest)
    dest.mkdir(exist_ok=True)
    
    # n_workers必須是1,因為我們在下載程序中檢查唯一的影像
    parallel(partial(_download_image_inner, dest, timeout=timeout, max_pics=max_pics), list(enumerate(urls)), n_workers=1)

def _download_image_inner(dest, inp, timeout=4, max_pics=150):
    # 輸入是一個列舉物件
    i,url = inp
    suffix = re.findall(r'\.\w+?(?=(?:\?|$))', url)
    suffix = suffix[0] if len(suffix)>0  else '.jpg'
    
    # 如果我們有足夠的圖片,什么都不用做,直到url用完
    if len(dest.ls()) >= max_pics:
      return

    # 函式處理base64編碼的影像
    # 如果抓取的url是已編碼的jpg格式,將其解碼并與其他格式一起保存
    try:
      if url[:15] == 'data:image/jpeg':
        encoded_image = url[url.find('/9'):]
        im = Image.open(io.BytesIO(base64.b64decode(encoded_image)))
        filehash = hashlib.md5(im.tobytes()).hexdigest()
        if filehash not in hash_keys: 
          hash_keys[filehash] = i
          im.save(dest/f"{i:08d}{suffix}")
        else:
          pass
    except:
      pass

    # 函式處理base64編碼的影像
    # 如果抓取的url是已編碼的png,將其解碼并將其與其余內容一起行內保存
    try:
      if url[:14] == 'data:image/png':
        encoded_image = url[url.find('iVBOR'):]
        im = Image.open(io.BytesIO(base64.standard_b64decode(encoded_image))).convert('RGB')
        filehash = hashlib.md5(im.tobytes()).hexdigest()
        if filehash not in hash_keys: 
          hash_keys[filehash] = i
          im.save(dest/f"{i:08d}{suffix}")
        else:
          pass
    except:
      pass

    # 如果抓取的url是一個http站點,下載它,并檢查我們還沒有得到相同的影像,
    try: 
      download_url(url, dest/f"{i:08d}{suffix}", overwrite=True, show_progress=True, timeout=timeout)
      im = Image.open(dest/f"{i:08d}{suffix}")
      filehash = hashlib.md5(im.tobytes()).hexdigest()
      if filehash not in hash_keys: 
        hash_keys[filehash] = i
      else:
        (dest/f"{i:08d}{suffix}").unlink()
    except Exception as e: f"Couldn't download {url}."

現在,我們可以遍歷我們的每一個科學植物名稱,收集它們的網址,然后下載這些圖片,同時驗證這些圖片是否是唯一的,每一組影像都下載到Colabs上我的鏈接驅動器中自己的檔案夾中,需要注意的一點是,由于google images上存在大量重復的圖片,要抓取的url數量必須遠遠大于你最終想要的圖片數量,

# 實體化webdriver
wd = webdriver.Chrome('chromedriver',options=options)
from tqdm.notebook import tqdm
import itertools
scientific_names = aspca_df['Scientific Name']

# 回圈所有室內植物的名字,抓取url并下載到我的谷歌驅動器
for name in tqdm(scientific_names):
  try:
    path = Path('/content/drive/My Drive/Houseplant Classifier/plant_images_deepest');
    folder = name
    dest = path/folder
    dest.mkdir(parents=True, exist_ok=True)
    if len(dest.ls())<150:
      print(f'{name} has {len(dest.ls())} images.')
      url_science = fetch_thumbnail_urls(f'{name}', max_links_to_fetch = 600, wd=wd, non_commercial = False, shuffle = False)
      dest = path/folder
    
      # 強制重繪hash_key—在函式中作為全域變數存盤,這里清空
      hash_keys = dict()
      download_images(path/folder, urls = url_science, max_pics=150) 
      print(f'Finished downloading images of {name} : {len(dest.ls())} images downloaded.') 
    else:
      print(f'{name} already has sufficient images.')
  except Exception as e:
    print(f'Error with {name}. {e}')

下載后,我們將采取步驟確保每個檔案夾包含正確數量的唯一影像,

因此,在這個階段,這些圖片被整齊地分到各自的檔案夾中,并直接放在我們的谷歌硬碟上,需要注意的是,如果你想用這些圖片來訓練CNN,如果你在使用它們之前把這些圖片帶到本地的Colab環境中,但這將在下一篇文章中進一步討論,

最后

從零開始構建資料庫影像分類專案對于簡單的玩具示例來說很簡單,參見fast.ai v2一個棕色/黑色/泰迪熊分類器的好例子(https://github.com/fastai/fastbook/blob/master/02_production.ipynb),對于這個專案,我想擴展相同的方法,但將其應用到更大的類集合中,這個程序實際上可以分為幾個步驟:

  1. 獲取類串列

由于beauthulsoup,從web頁面中獲取表格或文本資料非常簡單,通常只需要通過正則運算式或內置python方法進行更多處理,

清理和驗證下載資料的準確性是這一步中最大的挑戰,當我們有10個類和領域知識時,在繼續之前很容易發現錯誤并修復它們,當我們有500個類,事情就變得更難了,一個獨立的資料源是至關重要的,我們可以根據它來驗證我們的資料,在這種情況下,我們信任ASPCA資料中的毒性資訊,但不信任它們提供的學名,因此必須使用WFO資料庫對其進行更正,后者提供了最新的分類資訊,

  1. 獲取每個類的影像url串列

我們可以執行搜索,找到縮略圖并下載,甚至可以下載得到更大解析度的影像,

  1. 將每個影像下載到帶標簽的檔案夾中

用于下載影像的fastai函式運行良好,但是一個主要的絆腳石是下載重復的影像,如果你想要更多的圖片(10-15張),并且你下載了谷歌圖片搜索的所有結果,你很快就會得到大量的圖片副本,此外,該函式無法處理base64編碼的影像,值得慶幸的是,fastai提供了它們的源代碼,可以對其進行修改,以解釋編碼的影像以及下載http鏈接,下載后對它們進行哈希處理,并且只保留唯一的影像,

原文鏈接:https://towardsdatascience.com/creating-a-plant-pet-toxicity-classifier-a29587f3f04c

歡迎關注磐創AI博客站:
http://panchuang.net/

sklearn機器學習中文官方檔案:
http://sklearn123.com/

歡迎關注磐創博客資源匯總站:
http://docs.panchuang.net/

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/211579.html

標籤:其他

上一篇:使用NLP創建摘要

下一篇:Ubuntu 20.04安裝CUDA 11.0、cuDNN 8.0.5、PyTorch 1.7.0

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more