前往 ai.google.dev 查看 | 試用 Colab 筆記本 | 在 GitHub 中查看筆記本 |
總覽
本教學課程說明如何使用 Gemini API 的嵌入功能,偵測資料集中潛在的離群值。您將使用 t-SNE 視覺化 20 個 Newsgroup 資料集中的子集,然後偵測每個類別叢集中心點特定半徑範圍以外的離群值。
如要進一步瞭解如何透過 Gemini API 生成嵌入項目,請參閱 Python 快速入門導覽課程。
必要條件
您可以在 Google Colab 中執行本快速入門導覽課程。
如要在您的開發環境完成本快速入門導覽課程,請確保您的環境符合下列需求:
- Python 3.9 以上版本
- 安裝
jupyter
以執行筆記本。
設定
首先,下載並安裝 Gemini API Python 程式庫。
pip install -U -q google.generativeai
import re
import tqdm
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import google.generativeai as genai
# Used to securely store your API key
from google.colab import userdata
from sklearn.datasets import fetch_20newsgroups
from sklearn.manifold import TSNE
取得 API 金鑰
您必須先取得 API 金鑰,才能使用 Gemini API。如果您尚未建立金鑰,請在 Google AI Studio 中按一下滑鼠即可建立金鑰。
在 Colab 中,將金鑰新增至密鑰管理員下方的「🔑?」。輸入名稱 API_KEY
。
取得 API 金鑰後,請將其傳遞至 SDK。您可以透過兩種方法來選擇刊登位置:
- 將金鑰放入
GOOGLE_API_KEY
環境變數中 (SDK 會自動從中取得)。 - 將金鑰傳遞至
genai.configure(api_key=...)
genai.configure(api_key=GOOGLE_API_KEY)
for m in genai.list_models():
if 'embedContent' in m.supported_generation_methods:
print(m.name)
models/embedding-001 models/embedding-001
準備資料集
20 Newsgroups Text Dataset 包含 20 個主題的 18,000 個新聞群組貼文,分為訓練和測試集。訓練資料集和測試資料集的分割依據,是特定日期前後發布的訊息。本教學課程使用訓練子集。
newsgroups_train = fetch_20newsgroups(subset='train')
# View list of class names for dataset
newsgroups_train.target_names
['alt.atheism', 'comp.graphics', 'comp.os.ms-windows.misc', 'comp.sys.ibm.pc.hardware', 'comp.sys.mac.hardware', 'comp.windows.x', 'misc.forsale', 'rec.autos', 'rec.motorcycles', 'rec.sport.baseball', 'rec.sport.hockey', 'sci.crypt', 'sci.electronics', 'sci.med', 'sci.space', 'soc.religion.christian', 'talk.politics.guns', 'talk.politics.mideast', 'talk.politics.misc', 'talk.religion.misc']
這是訓練集中的第一個範例。
idx = newsgroups_train.data[0].index('Lines')
print(newsgroups_train.data[0][idx:])
Lines: 15 I was wondering if anyone out there could enlighten me on this car I saw the other day. It was a 2-door sports car, looked to be from the late 60s/ early 70s. It was called a Bricklin. The doors were really small. In addition, the front bumper was separate from the rest of the body. This is all I know. If anyone can tellme a model name, engine specs, years of production, where this car is made, history, or whatever info you have on this funky looking car, please e-mail. Thanks, - IL ---- brought to you by your neighborhood Lerxst ----
# Apply functions to remove names, emails, and extraneous words from data points in newsgroups.data
newsgroups_train.data = [re.sub(r'[\w\.-]+@[\w\.-]+', '', d) for d in newsgroups_train.data] # Remove email
newsgroups_train.data = [re.sub(r"\([^()]*\)", "", d) for d in newsgroups_train.data] # Remove names
newsgroups_train.data = [d.replace("From: ", "") for d in newsgroups_train.data] # Remove "From: "
newsgroups_train.data = [d.replace("\nSubject: ", "") for d in newsgroups_train.data] # Remove "\nSubject: "
# Cut off each text entry after 5,000 characters
newsgroups_train.data = [d[0:5000] if len(d) > 5000 else d for d in newsgroups_train.data]
# Put training points into a dataframe
df_train = pd.DataFrame(newsgroups_train.data, columns=['Text'])
df_train['Label'] = newsgroups_train.target
# Match label to target name index
df_train['Class Name'] = df_train['Label'].map(newsgroups_train.target_names.__getitem__)
df_train
接下來,請在訓練資料集裡取 150 個資料點,然後選擇幾個類別,對部分資料取樣。本教學課程使用科學類別。
# Take a sample of each label category from df_train
SAMPLE_SIZE = 150
df_train = (df_train.groupby('Label', as_index = False)
.apply(lambda x: x.sample(SAMPLE_SIZE))
.reset_index(drop=True))
# Choose categories about science
df_train = df_train[df_train['Class Name'].str.contains('sci')]
# Reset the index
df_train = df_train.reset_index()
df_train
df_train['Class Name'].value_counts()
sci.crypt 150 sci.electronics 150 sci.med 150 sci.space 150 Name: Class Name, dtype: int64
建立嵌入
本節將說明如何使用 Gemini API 的嵌入功能,為 DataFrame 中的不同文字產生嵌入。
將 API 變更為使用模型嵌入功能 001 的嵌入
新的嵌入模型「embedding-001」其中有新的工作類型參數和選用標題 (僅適用於 task_type=RETRIEVAL_DOCUMENT
)。
這些新參數僅適用於最新的嵌入模型。工作類型如下:
工作類型 | 說明 |
---|---|
RETRIEVAL_QUERY | 指定指定文字是搜尋/擷取設定中的查詢。 |
RETRIEVAL_DOCUMENT | 指定文字是搜尋/擷取設定中的文件。 |
SEMANTIC_SIMILARITY | 指定指定文字將用於語意文字相似度 (STS)。 |
分類 | 指定要將嵌入用於分類。 |
叢集 | 指定嵌入將用於分群。 |
from tqdm.auto import tqdm
tqdm.pandas()
from google.api_core import retry
def make_embed_text_fn(model):
@retry.Retry(timeout=300.0)
def embed_fn(text: str) -> list[float]:
# Set the task_type to CLUSTERING.
embedding = genai.embed_content(model=model,
content=text,
task_type="clustering")['embedding']
return np.array(embedding)
return embed_fn
def create_embeddings(df):
model = 'models/embedding-001'
df['Embeddings'] = df['Text'].progress_apply(make_embed_text_fn(model))
return df
df_train = create_embeddings(df_train)
df_train.drop('index', axis=1, inplace=True)
0%| | 0/600 [00:00<?, ?it/s]
降低維度
文件嵌入向量的維度為 768。為了視覺化呈現內嵌文件的分組方式,您必須套用維度縮減功能,因為您只能在 2D 或 3D 空間中視覺化嵌入嵌入。與內容相似的文件相比,內容相似的文件應放在空間較近的地方。
len(df_train['Embeddings'][0])
768
# Convert df_train['Embeddings'] Pandas series to a np.array of float32
X = np.array(df_train['Embeddings'].to_list(), dtype=np.float32)
X.shape
(600, 768)
您將應用 t-DistributedStochastic Neighbor Embedding (t-SNE) 方法來縮減維度。這項技巧可以減少維度數量,同時保留叢集 (相近距離的資料點)。針對原始資料,模型會嘗試建構分佈,並與其他資料點「相鄰」(舉例來說,兩者的意義相近)。接著會最佳化目標函式,在圖表中保持類似的分佈。
tsne = TSNE(random_state=0, n_iter=1000)
tsne_results = tsne.fit_transform(X)
df_tsne = pd.DataFrame(tsne_results, columns=['TSNE1', 'TSNE2'])
df_tsne['Class Name'] = df_train['Class Name'] # Add labels column from df_train to df_tsne
df_tsne
fig, ax = plt.subplots(figsize=(8,6)) # Set figsize
sns.set_style('darkgrid', {"grid.color": ".6", "grid.linestyle": ":"})
sns.scatterplot(data=df_tsne, x='TSNE1', y='TSNE2', hue='Class Name', palette='Set2')
sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
plt.title('Scatter plot of news using t-SNE')
plt.xlabel('TSNE1')
plt.ylabel('TSNE2');
離群值偵測
如要判斷哪些點異常,請判斷哪些點是離群值和離群值。請先找出代表集群中心的質心或位置,然後使用距離判斷離群值的點。
先取得每個類別的質心。
def get_centroids(df_tsne):
# Get the centroid of each cluster
centroids = df_tsne.groupby('Class Name').mean()
return centroids
centroids = get_centroids(df_tsne)
centroids
def get_embedding_centroids(df):
emb_centroids = dict()
grouped = df.groupby('Class Name')
for c in grouped.groups:
sub_df = grouped.get_group(c)
# Get the centroid value of dimension 768
emb_centroids[c] = np.mean(sub_df['Embeddings'], axis=0)
return emb_centroids
emb_c = get_embedding_centroids(df_train)
將您找到的每個群集資料與其餘點進行描繪。
# Plot the centroids against the cluster
fig, ax = plt.subplots(figsize=(8,6)) # Set figsize
sns.set_style('darkgrid', {"grid.color": ".6", "grid.linestyle": ":"})
sns.scatterplot(data=df_tsne, x='TSNE1', y='TSNE2', hue='Class Name', palette='Set2');
sns.scatterplot(data=centroids, x='TSNE1', y='TSNE2', color="black", marker='X', s=100, label='Centroids')
sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
plt.title('Scatter plot of news using t-SNE with centroids')
plt.xlabel('TSNE1')
plt.ylabel('TSNE2');
選擇半徑。只要有任何超出該類別質心的值,即視為離群值。
def calculate_euclidean_distance(p1, p2):
return np.sqrt(np.sum(np.square(p1 - p2)))
def detect_outlier(df, emb_centroids, radius):
for idx, row in df.iterrows():
class_name = row['Class Name'] # Get class name of row
# Compare centroid distances
dist = calculate_euclidean_distance(row['Embeddings'],
emb_centroids[class_name])
df.at[idx, 'Outlier'] = dist > radius
return len(df[df['Outlier'] == True])
range_ = np.arange(0.3, 0.75, 0.02).round(decimals=2).tolist()
num_outliers = []
for i in range_:
num_outliers.append(detect_outlier(df_train, emb_c, i))
# Plot range_ and num_outliers
fig = plt.figure(figsize = (14, 8))
plt.rcParams.update({'font.size': 12})
plt.bar(list(map(str, range_)), num_outliers)
plt.title("Number of outliers vs. distance of points from centroid")
plt.xlabel("Distance")
plt.ylabel("Number of outliers")
for i in range(len(range_)):
plt.text(i, num_outliers[i], num_outliers[i], ha = 'center')
plt.show()
您可以根據想要的異常偵測工具的靈敏度,選擇要使用的半徑範圍。目前系統使用的是 0.62,但您可以變更這個值。
# View the points that are outliers
RADIUS = 0.62
detect_outlier(df_train, emb_c, RADIUS)
df_outliers = df_train[df_train['Outlier'] == True]
df_outliers.head()
# Use the index to map the outlier points back to the projected TSNE points
outliers_projected = df_tsne.loc[df_outliers['Outlier'].index]
繪製離群值,並以透明的紅色來標示離群值。
fig, ax = plt.subplots(figsize=(8,6)) # Set figsize
plt.rcParams.update({'font.size': 10})
sns.set_style('darkgrid', {"grid.color": ".6", "grid.linestyle": ":"})
sns.scatterplot(data=df_tsne, x='TSNE1', y='TSNE2', hue='Class Name', palette='Set2');
sns.scatterplot(data=centroids, x='TSNE1', y='TSNE2', color="black", marker='X', s=100, label='Centroids')
# Draw a red circle around the outliers
sns.scatterplot(data=outliers_projected, x='TSNE1', y='TSNE2', color='red', marker='o', alpha=0.5, s=90, label='Outliers')
sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
plt.title('Scatter plot of news with outliers projected with t-SNE')
plt.xlabel('TSNE1')
plt.ylabel('TSNE2');
使用 Datafames 的索引值列出幾個各類別離群值可能的示例。這裡會顯示每個類別的第一個資料點。探索各類別的其他資料點,即可查看系統視為離群值或異常狀況的資料。
sci_crypt_outliers = df_outliers[df_outliers['Class Name'] == 'sci.crypt']
print(sci_crypt_outliers['Text'].iloc[0])
Re: Source of random bits on a Unix workstation Lines: 44 Nntp-Posting-Host: sandstorm >>For your application, what you can do is to encrypt the real-time clock >>value with a secret key. Well, almost.... If I only had to solve the problem for myself, and were willing to have to type in a second password whenever I logged in, it could work. However, I'm trying to create a solution that anyone can use, and which, once installed, is just as effortless to start up as the non-solution of just using xhost to control access. I've got religeous problems with storing secret keys on multiuser computers. >For a good discussion of cryptographically "good" random number >generators, check out the draft-ietf-security-randomness-00.txt >Internet Draft, available at your local friendly internet drafts >repository. Thanks for the pointer! It was good reading, and I liked the idea of using several unrelated sources with a strong mixing function. However, unless I missed something, the only source they suggested that seems available, and unguessable by an intruder, when a Unix is fresh-booted, is I/O buffers related to network traffic. I believe my solution basically uses that strategy, without requiring me to reach into the kernel. >A reasonably source of randomness is the output of a cryptographic >hash function , when fed with a large amount of >more-or-less random data. For example, running MD5 on /dev/mem is a >slow, but random enough, source of random bits; there are bound to be >128 bits of entropy in the tens of megabytes of data in >a modern workstation's memory, as a fair amount of them are system >timers, i/o buffers, etc. I heard about this solution, and it sounded good. Then I heard that folks were experiencing times of 30-60 seconds to run this, on reasonably-configured workstations. I'm not willing to add that much delay to someone's login process. My approach takes a second or two to run. I'm considering writing the be-all and end-all of solutions, that launches the MD5, and simultaneously tries to suck bits off the net, and if the net should be sitting __SO__ idle that it can't get 10K after compression before MD5 finishes, use the MD5. This way I could have guaranteed good bits, and a deterministic upper bound on login time, and still have the common case of login take only a couple of extra seconds. -Bennett
sci_elec_outliers = df_outliers[df_outliers['Class Name'] == 'sci.electronics']
print(sci_elec_outliers['Text'].iloc[0])
Re: Laser vs Bubblejet? Reply-To: Distribution: world X-Mailer: cppnews \\(Revision: 1.20 \\) Organization: null Lines: 53 Here is a different viewpoint. > FYI: The actual horizontal dot placement resoution of an HP > deskjet is 1/600th inch. The electronics and dynamics of the ink > cartridge, however, limit you to generating dots at 300 per inch. > On almost any paper, the ink wicks more than 1/300th inch anyway. > > The method of depositing and fusing toner of a laster printer > results in much less spread than ink drop technology. In practice there is little difference in quality but more care is needed with inkjet because smudges etc. can happen. > It doesn't take much investigation to see that the mechanical and > electronic complement of a laser printer is more complex than > inexpensive ink jet printers. Recall also that laser printers > offer a much higher throughput: 10 ppm for a laser versus about 1 > ppm for an ink jet printer. A cheap laser printer does not manage that sort of throughput and on top of that how long does the _first_ sheet take to print? Inkjets are faster than you say and in both cases the computer often has trouble keeping up with the printer. A sage said to me: "Do you want one copy or lots of copies?", "One", "Inkjet". > Something else to think about is the cost of consumables over the > life of the printer. A 3000 page yield toner cartridge is about > $US 75-80 at discount while HP high capacity > cartridges are about $US 22 at discount. It could be that over the > life cycle of the printer that consumables for laser printers are > less than ink jet printers. It is getting progressively closer > between the two technologies. Laser printers are usually desinged > for higher duty cycles in pages per month and longer product > replacement cycles. Paper cost is the same and both can use refills. Long term the laserprinter will need some expensive replacement parts and on top of that are the amortisation costs which favour the lowest purchase cost printer. HP inkjets understand PCL so in many cases a laserjet driver will work if the software package has no inkjet driver. There is one wild difference between the two printers: a laserprinter is a page printer whilst an inkjet is a line printer. This means that a laserprinter can rotate graphic images whilst an inkjet cannot. Few drivers actually use this facility. TC. E-mail: or
sci_med_outliers = df_outliers[df_outliers['Class Name'] == 'sci.med']
print(sci_med_outliers['Text'].iloc[0])
Re: THE BACK MACHINE - Update Organization: University of Nebraska--Lincoln Lines: 15 Distribution: na NNTP-Posting-Host: unlinfo.unl.edu I have a BACK MACHINE and have had one since January. While I have not found it to be a panacea for my back pain, I think it has helped somewhat. It MAINLY acts to stretch muscles in the back and prevent spasms associated with pain. I am taking less pain medication than I was previously. The folks at BACK TECHNOLOGIES are VERY reluctant to honor their return policy. They extended my "warranty" period rather than allow me to return the machine when, after the first month or so, I was not thrilled with it. They encouraged me to continue to use it, abeit less vigourously. Like I said, I can't say it is a cure-all, but it keeps me stretched out and I am in less pain. -- *********************************************************************** Dale M. Webb, DVM, PhD * 97% of the body is water. The Veterinary Diagnostic Center * other 3% keeps you from drowning. University of Nebraska, Lincoln *
sci_space_outliers = df_outliers[df_outliers['Class Name'] == 'sci.space']
print(sci_space_outliers['Text'].iloc[0])
MACH 25 landing site bases? Article-I.D.: aurora.1993Apr5.193829.1 Organization: University of Alaska Fairbanks Lines: 7 Nntp-Posting-Host: acad3.alaska.edu The supersonic booms hear a few months ago over I belive San Fran, heading east of what I heard, some new super speed Mach 25 aircraft?? What military based int he direction of flight are there that could handle a Mach 25aircraft on its landing decent?? Odd question?? == Michael Adams, -- I'm not high, just jacked
後續步驟
您已成功使用嵌入項目建立異常偵測工具!請嘗試使用自己的文字資料,以嵌入的方式呈現這些物件,並選擇一些邊界,以便偵測離群值。您可以縮減維度,以完成視覺化步驟。請注意,t-SNE 很適合用來分群輸入,但可能需要較長的時間才能聚集,或卡在本地 Minima。如果遇到這個問題,您可以考慮採用的另一種技巧是主要成分分析 (PCA)。
如要進一步瞭解如何使用嵌入功能,請參閱下列其他教學課程: